1 Introduction

This guide takes you through two different work flows to create palaeogeographical maps and plot occurrences fossil taxa in R. All the code and data are in this GitHub repository: https://github.com/benjaminmoon/palaeomap_example

You can download and run this code directly on your machine, or follow along online at Binder where I’ve created an environment with all of the data you need. Click the link below to open this in your web browser.

Binder

The Binder link will open in your browser in the RStudio interface. Open the file palaeomap_labgroup.Rmd from the file browser to get started.

The RStudio interface similar to that you should see in Binder. Open the file palaeomap_labgroup.nb.html to get started.

2 Palaeogeographical Maps

Maps are essential when discussing occurrences of taxa, both in the present and the past. Chuck in the issues associated with continental drift, and it can be difficult to place the wider context of some fossil occurrences. ‘These marine fossils are found at the top of a mountain, but where were the oceans 150 million years ago?’ Here are a couple of recent examples discussing the spread of occurrences, in this case Jurassic marine reptiles.

As a first example, Gao et al. (2019) discuss the occurrences of Jurassic plesiosaurs in China.

Here, Zverkov, Grigoriev, and Danilov (2020) show occurrences of ichthyosaurs in the Early Jurassic.

Reconstructing global palaeogeography has been an ongoing task for many decades, and both local and global reconstructions and compendia have been made (e.g. Bradshaw et al. 1992). But the case of making maps to show many occurrences by hand can be difficult and time-consuming.

A few examples of groups that have made efforts to create palaeogeographical maps at various points through the Phanerozoic include Ron Blakey at Deep Time Maps (https://deeptimemaps.com) and Christopher Scotese leading the Paleomap Project (http://scotese.com).

DeepTimeMaps_Animation_Mollweide_Sample from 42 Degrees North Media on Vimeo.

Palaeogeography of the Late Jurassic, as reconstructed at Palaeomap Project.

While built from models of plate tectonics and continental drift, these are nonetheless individual time points in a continuous series and open to some interpretation.

2.1 GPlates

GPlates (https://www.gplates.org) is free software that reconstructs the movement of tectonic plates through geological history. Using models of plate movement, the configuration can be reconstructed at arbitrary points in the past. Onto this, the palaeoenvironments of the formations at their current positions can be overlain, and so the positions of landmasses, mountains, coastlines, and marine settings can be plotted long back in time.

The download from GPlates provides lots of example data and projects to show the various plates, tectonics, plumes and other information that can be included. New data are being added as research is published. Try, for instance, the DataBundleForNovices project (GplatesSampleData/DataBundleForNovices) to see a combined model of modern continents and ocean spreading over the last 410 million years.

The main GPlates window showing the DataBundleForNovices loaded.

Layers can be selected in this Gplates window to show and hide different features.

There is also a GPlates Web Service (GWS; http://gws.gplates.org) that can provide some of the same data without the software. I’ll look briefly at this first.

2.2 Exercises 1

  1. Find an example of a palaeogeographical map for your favourite time period.
  2. Have a look in the folder GplatesSampleData to see what other projects are included.

3 Automatic map plotting in R

A couple of packages already exist for using GPlates reconstructions to create palaeogeographical maps in R:

Both of these download from GWS. I’ve borrowed some of their functions for this lab group.

If you’re unfamiliar with RStudio or RMarkdown (.Rmd files), then the code sections are placed in ‘chunks’ like the one below. You can run individual lines by Cmd+Enter or Ctrl+Enter, or run the whole chunk using the little green arrow to the top right of each chunk. Try with this chunk below if you want.

library(broom)
library(dplyr)

Attaching package: 'dplyr'
The following objects are masked from 'package:stats':

    filter, lag
The following objects are masked from 'package:base':

    intersect, setdiff, setequal, union
library(forcats)
library(ggplot2)
Use suppressPackageStartupMessages() to eliminate package startup
messages
library(ggthemes)
library(mapproj)
Loading required package: maps
library(purrr)

Attaching package: 'purrr'
The following object is masked from 'package:maps':

    map
library(readr)
library(rgdal)
Loading required package: sp
rgdal: version: 1.5-23, (SVN revision 1121)
Geospatial Data Abstraction Library extensions to R successfully loaded
Loaded GDAL runtime: GDAL 3.1.4, released 2020/10/20
Path to GDAL shared files: /Library/Frameworks/R.framework/Versions/4.0/Resources/library/rgdal/gdal
GDAL binary built with GEOS: TRUE 
Loaded PROJ runtime: Rel. 6.3.1, February 10th, 2020, [PJ_VERSION: 631]
Path to PROJ shared files: /Library/Frameworks/R.framework/Versions/4.0/Resources/library/rgdal/proj
Linking to sp version:1.4-5
To mute warnings of possible GDAL/OSR exportToProj4() degradation,
use options("rgdal_show_exportToProj4_warnings"="none") before loading rgdal.
library(stringr)
library(tibble)
# library(tidyverse) # a quick way to load packages, but slow for Binder.

list.files("functions/", pattern = "\\.R", full.names = TRUE) %>%
  walk(source)

NB not all of the chunks below should be run on Binder as some of the code will take a long time or fail to execute.

3.1 GPlates Web Service

GWS can reconstruct the locations of points, coastlines, plates, paths, and features back into the past (https://github.com/GPlates/gplates_web_service_doc/wiki). It can also use a variety of different reconstructions. Most relevant is perhaps the Golonka (2007) model, which is often used for palaeocoordinate reconstruction in the Palaeobiology Database (PBDB).

The process is:

  1. Download outlines of the continental coastlines.
  2. Download outlines of the tectonic plates.
  3. Read and convert these into a format that R and ggplot2 can use,
  • this uses the packages rgdal, readr, and broom.
  1. Plot,
  • in this case with gpplot.

Example code only. Do not run. The code below does these steps by downloading the data, which will likely take some time.

#### DO NOT RUN THIS CODE ####
coastline_gws_url <-
  "http://gws.gplates.org/reconstruct/coastlines/?time=155&model=GOLONKA"
polygons_gws_url <-
  "http://gws.gplates.org/reconstruct/static_polygons/?time=155&model=GOLONKA"

kimmeridgian_coastlines <-
  rgdal::readOGR(coastline_gws_url) %>%
    broom::tidy()
kimmeridgian_polygons <-
  rgdal::readOGR(polygons_gws_url) %>%
    broom::tidy()

This chunk below replicates the above using data I’ve already downloaded from GWS and stored in the GitHub repository, and can be used on Binder. It has the same steps of loading and tidying the data, then includes the commands to plot with ggplot2.

Feel free to run this code.

# Load and tidy the data 
kimmeridgian_coastlines <-
  readOGR(
    "data/GWS/Matthews_etal_GPG_2016_Coastlines_reconstructed_155.00Ma.gmt"
  ) %>%
  tidy()
kimmeridgian_polygons <-
  readOGR(
    "data/GWS/Matthews_etal_GPG_2016_Polygons_reconstructed_155.00Ma.gmt"
  ) %>%
  tidy()

ggplot() +
  geom_map(
    data = kimmeridgian_polygons,
    map = kimmeridgian_polygons,
    aes(x = long, y = lat, map_id = id),
    fill = "#D8D8D8"
  ) +
  geom_map(
    data = kimmeridgian_coastlines,
    map = kimmeridgian_coastlines,
    aes(x = long, y = lat, map_id = id),
    colour = "#222222", fill = NA, size = 0.3
  ) +
  coord_map("mollweide") +
  map_border() +
  theme_map()
**Outlines of continental plates in the Kimmeridgian (155 Ma).** Plates in the continental model are shaded grey, while the modern coastlines are outlined. Data downloaded from the GPlates web service.

Outlines of continental plates in the Kimmeridgian (155 Ma). Plates in the continental model are shaded grey, while the modern coastlines are outlined. Data downloaded from the GPlates web service.

When plotting, the data are added as layers, each plotting over the other: plate polygons at the bottom then coastlines on top. I’ve also set the map projection to be Mollweide, but you can play with this and change it to others included in the mapproj package function mapproject. Options include "Gilbert", "fisheye", and "hex". (Use, for example, coord_map("fisheye", n = 0.7) to add extra arguments.)

Additionally I’ve added a border (map_border(), in the functions folder) and used a basic theme (theme_map()) to get a plain background.

3.2 How R plots these map data

The positions and extent of map areas from data in GPlates are stored as a series of polygons. Each region is outlined by a series of points that form a closed shape, filled with a particular colour. This is exported from GPlates as OGR-GMT format, with file extension .gmt, which lists each point that marks the corner of a polygon. In R, broom converts this to a tibble (similar to a data.frame) that has the x and y locations of each point, and a series of columns that identify which polygon that point belongs to. These ID columns are important to make sure that polygons contain the correct points and are closed properly. Without these, there would only be a single shape that would stretch across the whole globe and not make much sense.

3.3 Not quite right

GWS is convenient for data and access, but the results in the plot above show the current continental coastlines and the plates. This does not reflect the ancient coastlines that were really after. Also, many of the countries are cross-cut by the polygons that form the sections in the model, which are untidy and typically not visible.

We need to go to the next level: true palaeogeographical reconstructions. Fortunately GPlates offers that too.

3.4 Exercises 2

  1. Have a look at the GWS documentation. What address would you use to get reconstructed coastlines in the early Carboniferous using the Matthews et al. (2016) model?
  2. Open an example of the OGR-GMT data from the GWS, either from the data/GWS folder or by downloading it. Can you see how the polygons are stored? How are different polygons identified?
  3. Now look at the tidied version in R, i.e. kimmeridgian_coastlines or kimmeridgian_polygons. By default this will only show the first 10 rows, but you can see more with, for example, head(kimmeridgian_polygons, n = 50L). Can you see how the OGR-GMT data is transferred to this tidy format?
  4. Play around with the inputs to the plot above. How messy or colourful can you make this map? What options are there in theme_map()? (Hint: use ?theme_map to see.)

4 ‘True’ Palaeogeographical Outlines

Among the projects included with the GPlates download is the set of palaeogeographical reconstructions by Cao et al. (2017) for the last 402 my. These are based, and so should align nicely, with the Golonka (2007) model that is used for data in the PBDB, which we’ll get to below. These data are in GPlatesSampleData/FeatureCollections/Palaeogeography/Global/.

Cao et al. (2017) used a combination of previous palaeogeographical reconstructions, and updated the coastlines, mountains, and extent of land and ice caps using occurrence data from the PBDB. In a few locations, what had been reconstructed as land produced marine fossils, when accounting for the movement of plates, and vice versa.

These reconstructions include four layers that have the separated palaeoevonvironmental regions:

  • Shallow marine: shelf seas.
  • Land: terrestrial habitats outlined by coastlines.
  • Mountain: areas of mountain building and uplift.
  • Ice cap: polar regions that have substantial evidence for long term presence of ice.

These are given the abbreviations ‘sm,’ ‘l,’ ‘m’ and ‘i.’ It’s worth noting that the way these are built means that the layers have to be plotted in the order above – shallow marine first, ice cap last. There are various areas that overlap (Australia especially) so the order of overlay becomes important in making the correct boundaries visible.

4.1 Export from GPlates

The Cao et al. (2017) reconstructions aren’t available from GWS, but can be exported from GPlates directly using the menu item Reconstruction > Export… Like above I’ve already done this for an example data set, in this case using the Late Jurassic (Kimmeridgian, 155 Ma) as an example. To make your own, once in the export window:

  1. Select the time of export (can be set from the main window time).
    • Also an option to export a series at regular intervals.
  2. The layers to export
    1. select Add Export… under Export Data
    2. choose Reconstructed Geometries
    3. output as OGR-GMT
    4. choose single or multiple files
      • I prefer multiple: you get one file for each of sea/land/mountain/ice. This is the form that works with the code below.
    5. OK
  3. Change Target directory: to somewhere you can find.
  4. Begin Export

Export options from GPlates.

Layer selection in GPlates export. Select Reconstructed geometries, OGR-GMT, and multiple layers.

The example data are in data/palaeogeography as three .gmt files that describe the polygon outlines for each of the layers. Note that as there are no ice caps at this time, there are only three files/layers. Look at the Carboniferous of later Palaeogene to get some ice caps.

4.2 Read in the map data

I find it useful to tie together the layers and colours from the beginning of importing – this ensures keeping the layers in the order for plotting. The data files are:

  • lm_402_2_reconstructed_155.00Ma.gmt: landmass
  • m_402_2_reconstructed_155.00Ma.gmt: mountain
  • sm_402_2_reconstructed_155.00Ma.gmt: shallow marine

This short code snippet sets lists the layer names and assigns the colours for each layer on the map – they are similar to those shown in GPlates, but not quite the same. I’ve also used #DAD3FF for ice caps when needed. (Technically this isn’t used for the colours, that’s in the palaeogeog_map_niceties function, this is somewhat a remnant of a previous way that I made this.)

map_layers <-
  c(
    "Land"           = "#FFD23A",
    "Mountain"       = "#FF8D51",
    "Shallow marine" = "#45D8FF"
  )

With that ready, the following code reads and tidies the data (as for above), then adds a column (geog_layer) to identify the data layer (shallow marine, land, mountain) and joins all the data together. It’s very tidyverse based, so I’d suggest looking at https://www.tidyverse.org to see the rationale and how they link using the pipe (%>%). You can also see more about individual functions in the R help.

The final step is to create a column (polygon_id) to identify each region on the, which come after mountain map. I had a long issue where different continents would join up, or Australia’s land would always appear plotted underneath the sea. As described above, this is because the shapes on the map are plotted as separate polygons, and the IDs are repeated between each of the layers. We’ll use the polygon_id column to make sure that each polygon has a unique ID by joining the layer, group and id name from the tidied data.

polygon_data <-
  list.files("data/palaeogeography/", pattern = ".gmt", full.names = TRUE) %>%
    map(readOGR) %>%
    map(tidy) %>%
    map2(
      names(map_layers),
      ~ add_column(.x, geog_layer = .y)
    ) %>%
    bind_rows() %>%
    mutate(
      polygon_id = str_c(id, group, geog_layer) %>% as_factor(),
      geog_layer = factor(geog_layer, labels = names(map_layers))
    )
OGR data source with driver: OGR_GMT 
Source: "/Users/glbcm/GitHub/palaeomap_example/data/palaeogeography/lm_402_2_reconstructed_155.00Ma.gmt", layer: "lm_402_2_reconstructed_155.00Ma"
with 361 features
It has 20 fields
OGR data source with driver: OGR_GMT 
Source: "/Users/glbcm/GitHub/palaeomap_example/data/palaeogeography/m_402_2_reconstructed_155.00Ma.gmt", layer: "m_402_2_reconstructed_155.00Ma"
with 195 features
It has 20 fields
OGR data source with driver: OGR_GMT 
Source: "/Users/glbcm/GitHub/palaeomap_example/data/palaeogeography/sm_402_2_reconstructed_155.00Ma.gmt", layer: "sm_402_2_reconstructed_155.00Ma"
with 808 features
It has 20 fields
Regions defined for each Polygons
Regions defined for each Polygons
Regions defined for each Polygons

4.3 Arranging the map layers

At this point it’s important to have the map layers in the correct order, so that marine is plotted under land, which is plotted under mountain.

This piece of code does that for you, ensuring shallow marine is first (it was third in map_layers above) then assigning this order to the polygon_id column. It converts the polygon_id column to a factor and re-orders thee levels based on the reordered map_layers.

id_level_order <-
  map_layers[c(3, 1, 2)] %>%
    map(
      function(layr) polygon_data$polygon_id %>% levels() %>% str_which(layr)
    ) %>%
    unlist()

polygon_data <-
  polygon_data %>%
    mutate(
      polygon_id = fct_relevel(polygon_id, levels(polygon_id)[id_level_order])
    )

4.4 Plotting the map

Unfortunately, using the same plot layer method as for the GWS data above is a little excessive. You would have to add a new annotation for each layer (just about okay for three, tedious for more), which then multiplies when plotting reconstructions of many times.

# DO NOT RUN THIS CODE
ggplot() +
  geom_map() +
  geom_map() +
  geom_map() + …

Instead, you can use the function geom_polygon and have the different layers plotted automatically with the colours assigned above. The fill is the most important argument as that assigns the main bulk of the colour. The colour argument is the outline, which you shouldn’t use bas that will outline the map layers and change their shape. The colour argument will be used when plotting occurrence points (see below). Also having the ordered and correctly grouped polygon_id is important here as that determines how the individual polygons are grouped and closed, preventing shapes from spanning the globe, or incorrectly overlapping.

I’ll point out here that there’s quite a lot going on in the palaeogeog_map_niceties function at the end. This is a collection of theme options and a colour scale to show the map properly. In particular this adds the outline and the lines of the equator and tropics. I guess one thing to note is that the ‘tropics,’ in a biodiversity sense, may not be the same in the past as the present. Increased temperatures can change the latitudinal biodiversity gradient. Also, obliquity and precession change the latitudes of the astronomical tropics through time.

# dev.new(width = 7, height = 4)
map_plot <-
  ggplot() +
    geom_polygon(
      data = polygon_data,
      aes(
        x      = long,
        y      = lat,
        fill   = geog_layer,
        group  = polygon_id
      ),
      colour = NA
    ) +
    coord_map("mollweide") +
    theme_map() +
    palaeogeog_map_niceties()
map_plot
**Palaeogeography in the Kimmeridgian (155 Ma).** Palaeogeographical reconstruction from @Cao2017Bb. Lines of latitude indicate the locations of the Equator and Tropics (±23.5°).

Palaeogeography in the Kimmeridgian (155 Ma). Palaeogeographical reconstruction from Cao et al. (2017). Lines of latitude indicate the locations of the Equator and Tropics (±23.5°).

You can try changing the look by modifying the projection (coord_map) and play about with some of the colours and markings by editing the function palaeogeog_map_niceties (functions/palaeogeog_map_niceties.R). You can also add further annotations (https://ggplot2-book.org/annotations.html#annotations) or change labels as you wish.

4.5 Going further 1: locating with modern coastlines

You may also want to add outlines of modern coastlines to show where these are and help locating. Again these are not available from GWS, but can be exported from GPlates. In GPlates, from File > Open Feature Collection… go to GplatesSampleData/FeatureCollections/Coastlines and load Matthews_etal_GPC_2016_Coastlines_Polyline.gpmlz. This adds a new layer into GPlates with areas of modern coastline in their relative positions for the time. Not all the modern coastlines are there as not all of these have certain locations, or rock to be found at – these come and go at different time points.

Like above, you can export this layer from GPlates. In this repo, I’ve already done that to data/coastlines/, which you can load with this code.

modern_coastlines <-
  readOGR("data/coastlines/Matthews_etal_GPC_2016_Coastlines_Polyline_reconstructed_155.00Ma.gmt") %>%
    tidy() %>%
    add_column(geog_layer = "Modern coastlines") %>%
    mutate(polygon_id = str_c(id, group, geog_layer) %>% as_factor())

This layer needs the same organising of IDs as the palaeogeography above, but once done can be easily added to map_plot above. This is where it’s useful to save your ggplot to an object alongside printing it – this you can just add new layers on top of the base map.

map_plot +
  geom_path(
    data = modern_coastlines,
    aes(
      x = long,
      y = lat,
      group = polygon_id,
    ),
    colour = "#888888", size = 0.3
  )
**Palaeogeography in the Kimmeridgian (155 Ma).** Outlines of modern coastlines (where known) are included in grey. Palaeogeographical reconstruction from @Cao2017Bb. Lines of latitude indicate the locations of the Equator and Tropics (±23.5°).

Palaeogeography in the Kimmeridgian (155 Ma). Outlines of modern coastlines (where known) are included in grey. Palaeogeographical reconstruction from Cao et al. (2017). Lines of latitude indicate the locations of the Equator and Tropics (±23.5°).

4.6 Exercises 3

  1. Try plotting this map with different projections (see above and ?mapproject). Does this work well for all projections? Why?
  2. Look at the code behind palaeogeog_map_niceties() (in functions/palaeogeog_map_niceties.R). Modify this to add different lines of latitude. Can you add lines of longitude too? What about changing the colour?
  • ggplot objects can have new layers added to them one-by-one by using +, but you can also combine them into function containing a list of layers that can be added in a single line.
  • Hint: to change the colour of the map layers, use fill.
  1. Can you try creating your own palaeogeographical map for your favourite time? This should be done on your own computer. Follow the instructions above to export data from GPlates then plot in R, making sure you change all the file and object names.

5 Adding Fossil Occurrences

5.1 Getting occurrence data

Now that you’ve spent lots of time making a pretty map, let’s plot some fossils on top of it.

Of course, being me and having used Jurassic data, we’re going to plot some ichthyosaurs. In this instance, the occurrence data comes from the PBDB, downloading ichthyosaur occurrences with these filters:

  • Callovian–Tithonian (166-145 Ma)
  • all levels of taxonomy – no filtering.

The PBDB has its own web API (https://paleobiodb.org/data1.2) from which you can get occurrences, collections, taxonomy and other things. In the code chunk below I’ve crafted the URL to download the data, but this is already in the repo so doesn’t need to be run.

Do not run the following chunk, the data is already in the repo.

#### DO NOT RUN THIS CHUNK ####
pbdb_url <-
  "https://paleobiodb.org/data1.2/occs/list.csv?base_name=Ichthyosauromorpha&interval=Callovian,Oxfordian,Kimmeridgian,Tithonian&show=paleoloc"

occ_ichthyosaurs <-
  read_csv(pbdb_url)

The part of the URL show=paleoloc is the important bit as the PBDB has already computed the palaeocoordinates of most occurrences using their modern location and the ages of the occurrences. Conveniently using the same default GPlates model as the palaeogeography.

5.2 Plotting occurrences

With that all done, use the chunk below to add the occurrences onto the base map. These are plotted with geom_point(), which you may be familiar with, setting the x and y values to the palaeolongitude and palaeolatitude respectively.

occ_ichthyosaurs <-
  read_csv("data/occurrences/2021-02-18-ichthyosaur-occurrences.csv")

── Column specification ────────────────────────────────────────────────────
cols(
  .default = col_character(),
  occurrence_no = col_double(),
  reid_no = col_double(),
  flags = col_logical(),
  collection_no = col_double(),
  identified_no = col_double(),
  accepted_no = col_double(),
  max_ma = col_double(),
  min_ma = col_double(),
  reference_no = col_double(),
  paleolng = col_double(),
  paleolat = col_double()
)
ℹ Use `spec()` for the full column specifications.
occ_plot <-
  map_plot +
    geom_point(
      data = occ_ichthyosaurs,
      aes(
        x = paleolng,
        y = paleolat
      )
    )
occ_plot
**Occurrences of ichthyosaurs from the Callovian–Tithonian.** Palaeogeographical map shows the distribution of land in the Kimmeridgian (155 Ma) reconstructed by @Cao2017Bb. Occurrences of Ichthyosauromorpha are from the Palaeobiology Database.

Occurrences of ichthyosaurs from the Callovian–Tithonian. Palaeogeographical map shows the distribution of land in the Kimmeridgian (155 Ma) reconstructed by Cao et al. (2017). Occurrences of Ichthyosauromorpha are from the Palaeobiology Database.

The more eagle-eyed of you may notice that some of these marine ichthyosaur occurrences are located resolutely in the middle of landmasses. There are a few possible reasons for that:

  1. The palaeogeographical reconstruction is wrong.
  2. The modern location for the occurrence is wrong, so the palaeocoordinates are wrong.
  3. The age of the occurrence is wrong, so its reconstructed position is wrong.

These three are worth considering further.

Incorrect palaeogeographical reconstruction is perhaps the most obvious answer: these reconstructions are difficult and there will always be some uncertainty with reconstructing the earth 155 million years ago. However, the method of Cao et al. (2017) included palaeoenvironments reconstructed using PBDB data to improve confidence in the borders of sea and land. The palaeogeography should match the PBDB well then, particularly in well-studied and sampled periods and locations. There are, however, some parts that cannot certainly reconstructed because of a lack of data, rock availability, or accessibility.

Incorrect modern location may be the easiest to discount as localities tend to be spatially constrained and identifiable, even in older literature. This being the case, there should only be minor error in the locations of occurrences (~10s km).

Incorrect age assignment is probably the most likely cause, or perhaps better imprecise ages. The palaeocoordinates calculated for each occurrence in the PBDB use the midpoint of its temporal range. Sometimes this range can be quite large, such as the several species of Undorosaurus that cover the whole Late Jurassic (163.5–145 Ma in GTS 2020/03). Those specimens from Patagonia that are plotted in the middle of the land are also similarly imprecisely dated. Their palaeocoordinates are reconstructed to the middle Kimmeridgian, whereas they are likely later (given the occurrence of ichthyosaurs from similar locations) when there was a sea there to swim in.

As the ichthyosaur occurrences included over over 20 my of earth history there will be changes in palaeogeography, and some potentially imprecise dating.

The ways around this are to have more time slices with the occurrences plotted, but this complicates the code and any plots, and such precise reconstructions may not be available. Alternatively, aim to use only the most precisely dated occurrences, if possible, but this can remove lots of useful data. Ultimately, you may just have to accept that some marine occurrences may appear on land, or vice versa.

5.3 Going further 2: separating occurrences with facets

The final thing that you can try is the faceting power of ggplot2 to separate the different groups of occurrences. Below, this code splits out the occurrences by their accepted taxonomic rank. Most occurrences are just non-diagnostic vertebral material, so can’t be assigned to a family, genus or species. These are listed under ‘unranked_clade.’

Conveniently, creating facets is as simple as adding a single line.

occ_plot +
  facet_wrap(vars(accepted_rank), ncol = 2)
**Occurrences of ichthyosaurs in the Callovian–Tithonian separated by identified rank.** Palaeogeographical maps shows distribution of land in the Kimmeridgian (155 Ma) reconstructed by @Cao2017Bb.

Occurrences of ichthyosaurs in the Callovian–Tithonian separated by identified rank. Palaeogeographical maps shows distribution of land in the Kimmeridgian (155 Ma) reconstructed by Cao et al. (2017).

5.4 Exercises 4

  1. Look at the PBDB web API (https://paleobiodb.org/data1.2). What other options and data sets can you access with this? Can you craft a URL that will download occurrence data of Tithonian bivalves.
  • This download may not work on Binder, so you should do it in R on your own computer.
  1. Try adding colour or shapes to you occurrence points using the options in geom_point.
  2. Download and plot the palaeogeographical map and occurrences for your favourite group in your favourite stage. Again this should be done on your own computer. Beware it may take a while if you want to plot many occurrences.
  3. It’s also useful to separate out occurrences on the map, for instance, can you colour occurrences within the outside the tropics differently? say red in the tropics (±23.5°) and blue outside.
  • Hint: you can use the palaeocoordinates of the PBDB data to assign a group to each occurrence, use this to colour the points in geom_point.
  • Hint: use scale_colour_discrete to colour the points. See the code of palaeogeog_map_niceties (functions/palaeogeog_map_niceties.R) for an example of doing this.

6 Suggested solutions to exercises

Not all of the exercise questions have a single obvious solution, particularly ones where you pick your favourite period. But here are some code suggestions for you to check against.

6.1 Exercises 1

  1. Find an example of a palaeogeographical map for your favourite time period.
  • You can have a look at Deep Time Maps or Paleomap for easy examples, or you can find others in papers.
  1. Have a look in the folder GplatesSampleData to see what other projects are included.
  • The GplatesSampleData/FeatureCollections directory is the one to look at. It includes:
    • Other plate reconstructions.
    • Ocean boundaries.
    • Plate flow lines.
    • Large igneous provinces … among other things.

6.2 Exercises 2

  1. Have a look at the GWS documentation. What address would you use to get reconstructed coastlines in the early Carboniferous using the Matthews et al. (2016) model?
  • "https://gws.gplates.org/reconstruct/coastlines/?&time=350&model=MATTHEWS2016_mantle_ref"
  • You could also use "MATTHEWS2016_pmag_ref". These are two different reconstruction methods.
  1. Open an example of the OGR-GMT data from the GWS, either from the data/GWS folder or by downloading it. Can you see how the polygons are stored? How are different polygons identified?
  • The metadata at the top has lines beginning with # @, including describing how the data for each polygon is organised.
  • Each polygon has a line of metadata describing it and @P indicating it’s a polygon.
  • Sometimes without metadata for each polygon, they are indicated with numbers: @P1, @P2 etc.
  1. Now look at the tidied version in R, i.e. kimmeridgian_coastlines or kimmeridgian_polygons. By default this will only show the first 10 rows, but you can see more with, for example, head(kimmeridgian_polygons, n = 50L). Can you see how the OGR-GMT data is transferred to this tidy format?
  • The data is stored in a tibble with each point in a row and columns for the longitude and latitude of the points.
  • The order that the points should be plot and the piece, group, and id of the polygon that they belong to are also included to separate the shapes.
  • These ID columns are taken from the metadata for each polygon define in the input OGR-GMT file.
  1. Play around with the inputs to the plot above. How messy or colourful can you make this map? What options are there in theme_map()? (Hint: use ?theme_map to see.)
  • I’ll leave this to your imagination.

6.3 Exercises 3

  1. Try plotting this map with different projections (see above and ?mapproject). Does this work well for all projections? Why?
  • Some of the projections move the line of 0° longitude away from the centre or have polygons that cross from one side of the map to the other. This can sometimes lead to shapes being drawn right across the map. I’m not sure whether the best way to counteract this is in redrawing the polygons or something to do with the map projection.
  1. Look at the code behind palaeogeog_map_niceties() (in functions/palaeogeog_map_niceties.R). Modify this to add different lines of latitude. Can you add lines of longitude too? What about changing the colour?
  • In palaeogeog_map_niceties I stored the lines of latitude that I want as a tibble that describe line segments. New lines of latitude can be added by creating a new row with the value y set to your latitude. The lines are described as going from -180°–+180° longitude at a specific single latitude.
  • For lines of longitude, I’d suggest using a new geom_segment layer. Modify the code so that the y value covers the range of latitudes –90°–+90° at a specific longitude. Here’s an example drawing lines of longitude at -50°, 0° and 72°:
geom_segment(
  data = tibble(
    x = c(-50, 0, 72),
    y = rep(-90, 3),
    yend = rep(90, 3)
  ),
  aes(x = x, xend = x, y = y, yend = yend),
  colour = "grey70", size = 0.3
)
  1. Can you try creating your own palaeogeographical map for your favourite time? This should be done on your own computer. Follow the instructions above to export data from GPlates then plot in R, making sure you change all the file and object names.
  • here you’ll have to modify the code above for your own GPlates data. Remember to add in the ice caps to the layer if you pick a time with these, such as the Carboniferous or the Neogene.

6.4 Exercises 4

  1. Look at the PBDB web API (https://paleobiodb.org/data1.2). What other options and data sets can you access with this? Can you craft a URL that will download occurrence data of Tithonian bivalves.
  1. Try adding colour or shapes to your occurrence points using the options in geom_point.
  • Use the colour and shape arguments.
  1. Download and plot the palaeogeographical map and occurrences for your favourite group in your favourite stage. Again this should be done on your own computer. Beware it may take a while if you want to plot many occurrences.
  • Use the code above, but change the relevant URLs and data files to your own GPlates and PBDB data. Don’t forget to add ice caps if you have them.
  • Hopefully the PBDB API will be working as it wasn’t when I was writing this!
  1. It’s also useful to separate out occurrences on the map, for instance, can you colour occurrences within the outside the tropics differently? say red in the tropics (±23.5°) and blue outside.
  • You’ll need to assign a column of identifiers for tropical and non-tropical occurrences. Below is an example using the dplyr function case_when.
  • With that done it’ll fit right into the plotting code from earlier, but you will need to specify the colour argument. If you want to make it blue and red then you can add a scale.
ex_occ_ichthyosaurs <-
  occ_ichthyosaurs %>%
    mutate(
      tropical = case_when(
        paleolat <= 23.5 & paleolat >= -23.5 ~ "tropical",
        paleolat > 23.5 | paleolat < -23.5   ~ "non-tropical"
      ) %>%
        as_factor()
    )

ex_occ_plot <-
  map_plot +
    geom_point(
      data = ex_occ_ichthyosaurs,
      aes(
        x = paleolng,
        y = paleolat,
        colour = tropical
      ),
      na.rm = TRUE
    ) +
    scale_discrete_manual(
      values = 
        c(
          "tropical" = "red",
          "non-tropical" = "blue"
        ),
        aesthetics = c("colour"),
        name = "Latitude"
      )
ex_occ_plot
**Occurrences of ichthyosaurs in the Callovian–Tithonian indicated by latitude region.** Palaeogeographical maps shows distribution of land in the Kimmeridgian (155 Ma) reconstructed by @Cao2017Bb.

Occurrences of ichthyosaurs in the Callovian–Tithonian indicated by latitude region. Palaeogeographical maps shows distribution of land in the Kimmeridgian (155 Ma) reconstructed by Cao et al. (2017).

7 References

Bradshaw, M J, John Christopher Wolverson Cope, D W Cripps, D T Donovan, Michael Kingsley Howarth, P F Rawson, I M West, and William Albert Wimbledon. 1992. “Jurassic.” Geological Society, London, Memoirs 13 (1): 107–29. https://doi.org/10.1144/GSL.MEM.1992.013.01.12.
Cao, Wenchao, Sabin Zahirovic, Nicolas Flament, Simon Williams, Jan Golonka, and R. Dietmar Müller. 2017. “Improving global paleogeography since the late Paleozoic using paleobiology.” Biogeosciences 14 (23): 5425–39. https://doi.org/10.5194/bg-14-5425-2017.
Gao, Ting, Da-Qing Li, Long-Feng Li, and Jing-Tao Yang. 2019. “The First Record of Freshwater Plesiosaurian from the Middle Jurassic of Gansu, NW China, with Its Implications to the Local Palaeobiogeography.” Journal of Palaeogeography, August. https://doi.org/10.1186/s42501-019-0043-5.
Golonka, J. 2007. “Phanerozoic Paleoenvironment and Paleolithofacies Maps: Mesozoic.” Geologia 33 (2): 211–64. https://www.infona.pl//resource/bwmeta1.element.baztech-article-AGH5-0014-0139.
Matthews, Kara J., Kayla T. Maloney, Sabin Zahirovic, Simon E. Williams, Maria Seton, and R. Dietmar Müller. 2016. “Global Plate Boundary Evolution and Kinematics Since the Late Paleozoic.” Global and Planetary Change 146 (November): 226–50. https://doi.org/10.1016/j.gloplacha.2016.10.002.
Zverkov, Nikolay G., Dmitry V. Grigoriev, and Igor G. Danilov. 2020. “Early Jurassic Palaeopolar Marine Reptiles of Siberia.” Geological Magazine, 1–18. https://doi.org/10.1017/S0016756820001351.
LS0tCnRpdGxlOiAiUGxvdHRpbmcgUGFsYWVvZ2VvZ3JhcGhpY2FsIE1hcHMgaW4gUjogYSBMYWIgR3JvdXAiCmF1dGhvcjogI0JlbiBNb29uCiAgLSBuYW1lOiBCZW4gTW9vbgogICAgYWZmaWxpYXRpb246IFBhbGFlb2Jpb2xvZ3kgUmVzZWFyY2ggR3JvdXAsIFNjaG9vbCBvZiBFYXJ0aCBTY2llbmNlcywgVW5pdmVyc2l0eSBvZiBCcmlzdG9sLCBCcmlzdG9sLCBVLksuCiAgICBlbWFpbDogYmVuamFtaW4ubW9vbkBicmlzdG9sLmFjLnVrCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVCICVZJylgIgpmaWdfY2FwdGlvbjogdHJ1ZQpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIGRmX3ByaW50OiBwYWdlZAogICAgY29kZV9mb2xkaW5nOiBzaG93IAogICAgIyBzZWxmX2NvbnRhaW5lZDogZmFsc2UKICAgICMga2VlcF9tZDogdHJ1ZQogICMgcGRmX2RvY3VtZW50OgogICMgICBrZWVwX3RleDogdHJ1ZQpiaWJsaW9ncmFwaHk6IGJpYmxpb2dyYXBoeS55YW1sCi0tLQoKIyBJbnRyb2R1Y3Rpb24gIwoKVGhpcyBndWlkZSB0YWtlcyB5b3UgdGhyb3VnaCB0d28gZGlmZmVyZW50IHdvcmsgZmxvd3MgdG8gY3JlYXRlIHBhbGFlb2dlb2dyYXBoaWNhbCBtYXBzIGFuZCBwbG90IG9jY3VycmVuY2VzIGZvc3NpbCB0YXhhIGluIFIuIEFsbCB0aGUgY29kZSBhbmQgZGF0YSBhcmUgaW4gdGhpcyBHaXRIdWIgcmVwb3NpdG9yeTogPGh0dHBzOi8vZ2l0aHViLmNvbS9iZW5qYW1pbm1vb24vcGFsYWVvbWFwX2V4YW1wbGU+CgpZb3UgY2FuIGRvd25sb2FkIGFuZCBydW4gdGhpcyBjb2RlIGRpcmVjdGx5IG9uIHlvdXIgbWFjaGluZSwgb3IgZm9sbG93IGFsb25nIG9ubGluZSBhdCBCaW5kZXIgd2hlcmUgSSd2ZSBjcmVhdGVkIGFuIGVudmlyb25tZW50IHdpdGggYWxsIG9mIHRoZSBkYXRhIHlvdSBuZWVkLiBDbGljayB0aGUgbGluayBiZWxvdyB0byBvcGVuIHRoaXMgaW4geW91ciB3ZWIgYnJvd3Nlci4KClshW0JpbmRlcl0oaHR0cHM6Ly9teWJpbmRlci5vcmcvYmFkZ2VfbG9nby5zdmcpXShodHRwczovL215YmluZGVyLm9yZy92Mi9naC9iZW5qYW1pbm1vb24vcGFsYWVvbWFwX2V4YW1wbGUvSEVBRD91cmxwYXRoPXJzdHVkaW8pCgpUaGUgQmluZGVyIGxpbmsgd2lsbCBvcGVuIGluIHlvdXIgYnJvd3NlciBpbiB0aGUgUlN0dWRpbyBpbnRlcmZhY2UuIE9wZW4gdGhlIGZpbGUgYHBhbGFlb21hcF9sYWJncm91cC5SbWRgIGZyb20gdGhlIGZpbGUgYnJvd3NlciB0byBnZXQgc3RhcnRlZC4KCiFbVGhlIFJTdHVkaW8gaW50ZXJmYWNlIHNpbWlsYXIgdG8gdGhhdCB5b3Ugc2hvdWxkIHNlZSBpbiBCaW5kZXIuIE9wZW4gdGhlIGZpbGUgYHBhbGFlb21hcF9sYWJncm91cC5uYi5odG1sYCB0byBnZXQgc3RhcnRlZC5dKC4vZmlncy9yc3R1ZGlvX3dpbmRvdy5wbmcpCgoKIyBQYWxhZW9nZW9ncmFwaGljYWwgTWFwcyAjCgpNYXBzIGFyZSBlc3NlbnRpYWwgd2hlbiBkaXNjdXNzaW5nIG9jY3VycmVuY2VzIG9mIHRheGEsIGJvdGggaW4gdGhlIHByZXNlbnQgYW5kIHRoZSBwYXN0LiBDaHVjayBpbiB0aGUgaXNzdWVzIGFzc29jaWF0ZWQgd2l0aCBjb250aW5lbnRhbCBkcmlmdCwgYW5kIGl0IGNhbiBiZSBkaWZmaWN1bHQgdG8gcGxhY2UgdGhlIHdpZGVyIGNvbnRleHQgb2Ygc29tZSBmb3NzaWwgb2NjdXJyZW5jZXMuICdUaGVzZSBtYXJpbmUgZm9zc2lscyBhcmUgZm91bmQgYXQgdGhlIHRvcCBvZiBhIG1vdW50YWluLCBidXQgd2hlcmUgd2VyZSB0aGUgb2NlYW5zIDE1MCBtaWxsaW9uIHllYXJzIGFnbz8nIEhlcmUgYXJlIGEgY291cGxlIG9mIHJlY2VudCBleGFtcGxlcyBkaXNjdXNzaW5nIHRoZSBzcHJlYWQgb2Ygb2NjdXJyZW5jZXMsIGluIHRoaXMgY2FzZSBKdXJhc3NpYyBtYXJpbmUgcmVwdGlsZXMuCgohW0FzIGEgZmlyc3QgZXhhbXBsZSwgQEdhbzIwMTlKUCBkaXNjdXNzIHRoZSBvY2N1cnJlbmNlcyBvZiBKdXJhc3NpYyBwbGVzaW9zYXVycyBpbiBDaGluYS5dKGh0dHBzOi8vbWVkaWEuc3ByaW5nZXJuYXR1cmUuY29tL2Z1bGwvc3ByaW5nZXItc3RhdGljL2ltYWdlL2FydCUzQTEwLjExODYlMkZzNDI1MDEtMDE5LTAwNDMtNS9NZWRpYU9iamVjdHMvNDI1MDFfMjAxOV80M19GaWc0X0hUTUwucG5nKQoKIVtIZXJlLCBAWnZlcmtvdjIwMjBHTSBzaG93IG9jY3VycmVuY2VzIG9mIGljaHRoeW9zYXVycyBpbiB0aGUgRWFybHkgSnVyYXNzaWMuXShodHRwczovL3N0YXRpYy5jYW1icmlkZ2Uub3JnL2JpbmFyeS92ZXJzaW9uL2lkL3VybjpjYW1icmlkZ2Uub3JnOmlkOmJpbmFyeToyMDIwMTIyMzA5MTQ1Njk4NC0wMjUyOlMwMDE2NzU2ODIwMDAxMzUxOlMwMDE2NzU2ODIwMDAxMzUxX2ZpZzEucG5nKQoKUmVjb25zdHJ1Y3RpbmcgZ2xvYmFsIHBhbGFlb2dlb2dyYXBoeSBoYXMgYmVlbiBhbiBvbmdvaW5nIHRhc2sgZm9yIG1hbnkgZGVjYWRlcywgYW5kIGJvdGggbG9jYWwgYW5kIGdsb2JhbCByZWNvbnN0cnVjdGlvbnMgYW5kIGNvbXBlbmRpYSBoYXZlIGJlZW4gbWFkZSBbZS5nLiBAQnJhZHNoYXcxOTkyR1NMTV0uIEJ1dCB0aGUgY2FzZSBvZiBtYWtpbmcgbWFwcyB0byBzaG93IG1hbnkgb2NjdXJyZW5jZXMgYnkgaGFuZCBjYW4gYmUgZGlmZmljdWx0IGFuZCB0aW1lLWNvbnN1bWluZy4KCkEgZmV3IGV4YW1wbGVzIG9mIGdyb3VwcyB0aGF0IGhhdmUgbWFkZSBlZmZvcnRzIHRvIGNyZWF0ZSBwYWxhZW9nZW9ncmFwaGljYWwgbWFwcyBhdCB2YXJpb3VzIHBvaW50cyB0aHJvdWdoIHRoZSBQaGFuZXJvem9pYyBpbmNsdWRlIFJvbiBCbGFrZXkgYXQgX0RlZXAgVGltZSBNYXBzXyAoPGh0dHBzOi8vZGVlcHRpbWVtYXBzLmNvbT4pIGFuZCBDaHJpc3RvcGhlciBTY290ZXNlIGxlYWRpbmcgdGhlIF9QYWxlb21hcCBQcm9qZWN0XyAoPGh0dHA6Ly9zY290ZXNlLmNvbT4pLgoKPGlmcmFtZSBzcmM9Imh0dHBzOi8vcGxheWVyLnZpbWVvLmNvbS92aWRlby8zMTU5MDcxMDY/dGl0bGU9MCZieWxpbmU9MCZwb3J0cmFpdD0wIiB3aWR0aD0iNjQwIiBoZWlnaHQ9IjMyMCIgZnJhbWVib3JkZXI9IjAiIGFsbG93PSJhdXRvcGxheTsgZnVsbHNjcmVlbjsgcGljdHVyZS1pbi1waWN0dXJlIiBhbGxvd2Z1bGxzY3JlZW4+PC9pZnJhbWU+CjxwPjxhIGhyZWY9Imh0dHBzOi8vdmltZW8uY29tLzMxNTkwNzEwNiI+RGVlcFRpbWVNYXBzX0FuaW1hdGlvbl9Nb2xsd2VpZGVfU2FtcGxlPC9hPiBmcm9tIDxhIGhyZWY9Imh0dHBzOi8vdmltZW8uY29tLzQyZGVncmVlc25vcnRoIj40MiBEZWdyZWVzIE5vcnRoIE1lZGlhPC9hPiBvbiA8YSBocmVmPSJodHRwczovL3ZpbWVvLmNvbSI+VmltZW88L2E+LjwvcD4KCjwhLS0gIVtQYWxhZW9nZW9ncmFwaHkgaW4gdGhlIExhdGUgSnVyYXNzaWMsIGFzIHJlY29uc3RydWN0ZWQgYXQgX0RlZXAgVGltZQpNYXBzLl9dKGh0dHBzOi8vZGVlcHRpbWVtYXBzLmNvbS93cC1jb250ZW50L3VwbG9hZHMvMjAxOC8wNy8xNTAtTWEtTW9sbC1fSnVyLUdQVC1taW4tMS03Njh4MzgyLmpwZykKLS0+CgohW1BhbGFlb2dlb2dyYXBoeSBvZiB0aGUgTGF0ZSBKdXJhc3NpYywgYXMgcmVjb25zdHJ1Y3RlZCBhdCBfUGFsYWVvbWFwIFByb2plY3QuX10oaHR0cDovL3d3dy5zY290ZXNlLmNvbS9pbWFnZXMvMTUyLmpwZykKCldoaWxlIGJ1aWx0IGZyb20gbW9kZWxzIG9mIHBsYXRlIHRlY3RvbmljcyBhbmQgY29udGluZW50YWwgZHJpZnQsIHRoZXNlIGFyZSBub25ldGhlbGVzcyBpbmRpdmlkdWFsIHRpbWUgcG9pbnRzIGluIGEgY29udGludW91cyBzZXJpZXMgYW5kIG9wZW4gdG8gc29tZSBpbnRlcnByZXRhdGlvbi4KCiMjIEdQbGF0ZXMgIyMKCkdQbGF0ZXMgKDxodHRwczovL3d3dy5ncGxhdGVzLm9yZz4pIGlzIGZyZWUgc29mdHdhcmUgdGhhdCByZWNvbnN0cnVjdHMgdGhlIG1vdmVtZW50IG9mIHRlY3RvbmljIHBsYXRlcyB0aHJvdWdoIGdlb2xvZ2ljYWwgaGlzdG9yeS4gVXNpbmcgbW9kZWxzIG9mIHBsYXRlIG1vdmVtZW50LCB0aGUgY29uZmlndXJhdGlvbiBjYW4gYmUgcmVjb25zdHJ1Y3RlZCBhdCBhcmJpdHJhcnkgcG9pbnRzIGluIHRoZSBwYXN0LiBPbnRvIHRoaXMsIHRoZSBwYWxhZW9lbnZpcm9ubWVudHMgb2YgdGhlIGZvcm1hdGlvbnMgYXQgdGhlaXIgY3VycmVudCBwb3NpdGlvbnMgY2FuIGJlIG92ZXJsYWluLCBhbmQgc28gdGhlIHBvc2l0aW9ucyBvZiBsYW5kbWFzc2VzLCBtb3VudGFpbnMsIGNvYXN0bGluZXMsIGFuZCBtYXJpbmUgc2V0dGluZ3MgY2FuIGJlIHBsb3R0ZWQgbG9uZyBiYWNrIGluIHRpbWUuCgpUaGUgZG93bmxvYWQgZnJvbSBHUGxhdGVzIHByb3ZpZGVzIGxvdHMgb2YgZXhhbXBsZSBkYXRhIGFuZCBwcm9qZWN0cyB0byBzaG93IHRoZSB2YXJpb3VzIHBsYXRlcywgdGVjdG9uaWNzLCBwbHVtZXMgYW5kIG90aGVyIGluZm9ybWF0aW9uIHRoYXQgY2FuIGJlIGluY2x1ZGVkLiBOZXcgZGF0YSBhcmUgYmVpbmcgYWRkZWQgYXMgcmVzZWFyY2ggaXMgcHVibGlzaGVkLiBUcnksIGZvciBpbnN0YW5jZSwgdGhlIF9EYXRhQnVuZGxlRm9yTm92aWNlc18gcHJvamVjdCAoYEdwbGF0ZXNTYW1wbGVEYXRhL0RhdGFCdW5kbGVGb3JOb3ZpY2VzYCkgdG8gc2VlIGEgY29tYmluZWQgbW9kZWwgb2YgbW9kZXJuIGNvbnRpbmVudHMgYW5kIG9jZWFuIHNwcmVhZGluZyBvdmVyIHRoZSBsYXN0IDQxMCBtaWxsaW9uIHllYXJzLgoKIVtUaGUgbWFpbiBHUGxhdGVzIHdpbmRvdyBzaG93aW5nIHRoZSBEYXRhQnVuZGxlRm9yTm92aWNlcyBsb2FkZWQuXSguL2ZpZ3MvZ3BsYXRlc193aW5kb3cucG5nKQoKIVtMYXllcnMgY2FuIGJlIHNlbGVjdGVkIGluIHRoaXMgR3BsYXRlcyB3aW5kb3cgdG8gc2hvdyBhbmQgaGlkZSBkaWZmZXJlbnQgZmVhdHVyZXMuXSguL2ZpZ3MvZ3BsYXRlc19sYXllcnMucG5nKQoKVGhlcmUgaXMgYWxzbyBhIEdQbGF0ZXMgV2ViIFNlcnZpY2UgKEdXUzsgPGh0dHA6Ly9nd3MuZ3BsYXRlcy5vcmc+KSB0aGF0IGNhbiBwcm92aWRlIHNvbWUgb2YgdGhlIHNhbWUgZGF0YSB3aXRob3V0IHRoZSBzb2Z0d2FyZS4gSSdsbCBsb29rIGJyaWVmbHkgYXQgdGhpcyBmaXJzdC4KCiMjIEV4ZXJjaXNlcyAxICMjCgoxLiBGaW5kIGFuIGV4YW1wbGUgb2YgYSBwYWxhZW9nZW9ncmFwaGljYWwgbWFwIGZvciB5b3VyIGZhdm91cml0ZSB0aW1lIHBlcmlvZC4KMi4gSGF2ZSBhIGxvb2sgaW4gdGhlIGZvbGRlciBgR3BsYXRlc1NhbXBsZURhdGFgIHRvIHNlZSB3aGF0IG90aGVyIHByb2plY3RzIGFyZSBpbmNsdWRlZC4gCgoKIyBBdXRvbWF0aWMgbWFwIHBsb3R0aW5nIGluIFIgIwoKQSBjb3VwbGUgb2YgcGFja2FnZXMgYWxyZWFkeSBleGlzdCBmb3IgdXNpbmcgR1BsYXRlcyByZWNvbnN0cnVjdGlvbnMgdG8gY3JlYXRlIHBhbGFlb2dlb2dyYXBoaWNhbCBtYXBzIGluIFI6CgoqIE5vbmFSL3BhbGVvTWFwICg8aHR0cHM6Ly9naXRodWIuY29tL05vbmFSL3BhbGVvTWFwPikKICAtIE5CIEhhcyBub3QgYmVlbiB1cGRhdGVkIGluIHNldmVyYWwgeWVhcnMuCiogTHVuYVNhcmUvZ3BsYXRlc3IgKDxodHRwczovL2dpdGh1Yi5jb20vTHVuYVNhcmUvZ3BsYXRlc3I+KQogIC0gTW9yZSByZWNlbnRseSB1cGRhdGVkLCBhbmQgc2ltaWxhciB0byBhYm92ZS4KCkJvdGggb2YgdGhlc2UgZG93bmxvYWQgZnJvbSBHV1MuIEkndmUgYm9ycm93ZWQgc29tZSBvZiB0aGVpciBmdW5jdGlvbnMgZm9yIHRoaXMgbGFiIGdyb3VwLgoKSWYgeW91J3JlIHVuZmFtaWxpYXIgd2l0aCBSU3R1ZGlvIG9yIFJNYXJrZG93biAoYC5SbWRgIGZpbGVzKSwgdGhlbiB0aGUgY29kZSBzZWN0aW9ucyBhcmUgcGxhY2VkIGluICdjaHVua3MnIGxpa2UgdGhlIG9uZSBiZWxvdy4gWW91IGNhbiBydW4gaW5kaXZpZHVhbCBsaW5lcyBieSBDbWQrRW50ZXIgb3IgQ3RybCtFbnRlciwgb3IgcnVuIHRoZSB3aG9sZSBjaHVuayB1c2luZyB0aGUgbGl0dGxlIGdyZWVuIGFycm93IHRvIHRoZSB0b3AgcmlnaHQgb2YgZWFjaCBjaHVuay4gVHJ5IHdpdGggdGhpcyBjaHVuayBiZWxvdyBpZiB5b3Ugd2FudC4KCmBgYHtyIHNldHVwfQpsaWJyYXJ5KGJyb29tKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGZvcmNhdHMpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3RoZW1lcykKbGlicmFyeShtYXBwcm9qKQpsaWJyYXJ5KHB1cnJyKQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KHJnZGFsKQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkodGliYmxlKQojIGxpYnJhcnkodGlkeXZlcnNlKSAjIGEgcXVpY2sgd2F5IHRvIGxvYWQgcGFja2FnZXMsIGJ1dCBzbG93IGZvciBCaW5kZXIuCgpsaXN0LmZpbGVzKCJmdW5jdGlvbnMvIiwgcGF0dGVybiA9ICJcXC5SIiwgZnVsbC5uYW1lcyA9IFRSVUUpICU+JQogIHdhbGsoc291cmNlKQpgYGAKCioqTkIgbm90IGFsbCBvZiB0aGUgY2h1bmtzIGJlbG93IHNob3VsZCBiZSBydW4gb24gQmluZGVyIGFzIHNvbWUgb2YgdGhlIGNvZGUgd2lsbCB0YWtlIGEgbG9uZyB0aW1lIG9yIGZhaWwgdG8gZXhlY3V0ZS4qKgoKIyMgR1BsYXRlcyBXZWIgU2VydmljZSAjIwoKR1dTIGNhbiByZWNvbnN0cnVjdCB0aGUgbG9jYXRpb25zIG9mIHBvaW50cywgY29hc3RsaW5lcywgcGxhdGVzLCBwYXRocywgYW5kIGZlYXR1cmVzIGJhY2sgaW50byB0aGUgcGFzdCAoPGh0dHBzOi8vZ2l0aHViLmNvbS9HUGxhdGVzL2dwbGF0ZXNfd2ViX3NlcnZpY2VfZG9jL3dpa2k+KS4gSXQgY2FuIGFsc28gdXNlIGEgdmFyaWV0eSBvZiBkaWZmZXJlbnQgcmVjb25zdHJ1Y3Rpb25zLiBNb3N0IHJlbGV2YW50IGlzIHBlcmhhcHMgdGhlIEBHb2xvbmthMjAwN0cgbW9kZWwsIHdoaWNoIGlzIG9mdGVuIHVzZWQgZm9yIHBhbGFlb2Nvb3JkaW5hdGUgcmVjb25zdHJ1Y3Rpb24gaW4gdGhlIFBhbGFlb2Jpb2xvZ3kgRGF0YWJhc2UgKFBCREIpLgoKVGhlIHByb2Nlc3MgaXM6CgoxLiBEb3dubG9hZCBvdXRsaW5lcyBvZiB0aGUgY29udGluZW50YWwgY29hc3RsaW5lcy4KMi4gRG93bmxvYWQgb3V0bGluZXMgb2YgdGhlIHRlY3RvbmljIHBsYXRlcy4KMy4gUmVhZCBhbmQgY29udmVydCB0aGVzZSBpbnRvIGEgZm9ybWF0IHRoYXQgUiBhbmQgX2dncGxvdDJfIGNhbiB1c2UsCiAgLSB0aGlzIHVzZXMgdGhlIHBhY2thZ2VzIF9yZ2RhbCwgcmVhZHIsXyBhbmQgX2Jyb29tLl8KNC4gUGxvdCwKICAtIGluIHRoaXMgY2FzZSB3aXRoIF9ncHBsb3QuXwoKKipFeGFtcGxlIGNvZGUgb25seS4gRG8gbm90IHJ1bi4qKiBUaGUgY29kZSBiZWxvdyBkb2VzIHRoZXNlIHN0ZXBzIGJ5IGRvd25sb2FkaW5nIHRoZSBkYXRhLCB3aGljaCB3aWxsIGxpa2VseSB0YWtlIHNvbWUgdGltZS4KCmBgYHtyIGd3c19kYXRhLCBldmFsID0gRkFMU0V9CiMjIyMgRE8gTk9UIFJVTiBUSElTIENPREUgIyMjIwpjb2FzdGxpbmVfZ3dzX3VybCA8LQogICJodHRwOi8vZ3dzLmdwbGF0ZXMub3JnL3JlY29uc3RydWN0L2NvYXN0bGluZXMvP3RpbWU9MTU1Jm1vZGVsPUdPTE9OS0EiCnBvbHlnb25zX2d3c191cmwgPC0KICAiaHR0cDovL2d3cy5ncGxhdGVzLm9yZy9yZWNvbnN0cnVjdC9zdGF0aWNfcG9seWdvbnMvP3RpbWU9MTU1Jm1vZGVsPUdPTE9OS0EiCgpraW1tZXJpZGdpYW5fY29hc3RsaW5lcyA8LQogIHJnZGFsOjpyZWFkT0dSKGNvYXN0bGluZV9nd3NfdXJsKSAlPiUKICAgIGJyb29tOjp0aWR5KCkKa2ltbWVyaWRnaWFuX3BvbHlnb25zIDwtCiAgcmdkYWw6OnJlYWRPR1IocG9seWdvbnNfZ3dzX3VybCkgJT4lCiAgICBicm9vbTo6dGlkeSgpCmBgYAoKVGhpcyBjaHVuayBiZWxvdyByZXBsaWNhdGVzIHRoZSBhYm92ZSB1c2luZyBkYXRhIEkndmUgYWxyZWFkeSBkb3dubG9hZGVkIGZyb20gR1dTIGFuZCBzdG9yZWQgaW4gdGhlIEdpdEh1YiByZXBvc2l0b3J5LCBhbmQgY2FuIGJlIHVzZWQgb24gQmluZGVyLiBJdCBoYXMgdGhlIHNhbWUgc3RlcHMgb2YgbG9hZGluZyBhbmQgdGlkeWluZyB0aGUgZGF0YSwgdGhlbiBpbmNsdWRlcyB0aGUgY29tbWFuZHMgdG8gcGxvdCB3aXRoIF9nZ3Bsb3QyLl8KCioqRmVlbCBmcmVlIHRvIHJ1biB0aGlzIGNvZGUuKioKCmBgYHtyIGd3c19wbG90LCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNCwgZmlnLmNhcCA9ICIqKk91dGxpbmVzIG9mIGNvbnRpbmVudGFsIHBsYXRlcyBpbiB0aGUgS2ltbWVyaWRnaWFuICgxNTUgTWEpLioqIFBsYXRlcyBpbiB0aGUgY29udGluZW50YWwgbW9kZWwgYXJlIHNoYWRlZCBncmV5LCB3aGlsZSB0aGUgbW9kZXJuIGNvYXN0bGluZXMgYXJlIG91dGxpbmVkLiBEYXRhIGRvd25sb2FkZWQgZnJvbSB0aGUgR1BsYXRlcyB3ZWIgc2VydmljZS4iLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgcmVzdWx0cyA9ICJoaWRlIn0KIyBMb2FkIGFuZCB0aWR5IHRoZSBkYXRhIApraW1tZXJpZGdpYW5fY29hc3RsaW5lcyA8LQogIHJlYWRPR1IoCiAgICAiZGF0YS9HV1MvTWF0dGhld3NfZXRhbF9HUEdfMjAxNl9Db2FzdGxpbmVzX3JlY29uc3RydWN0ZWRfMTU1LjAwTWEuZ210IgogICkgJT4lCiAgdGlkeSgpCmtpbW1lcmlkZ2lhbl9wb2x5Z29ucyA8LQogIHJlYWRPR1IoCiAgICAiZGF0YS9HV1MvTWF0dGhld3NfZXRhbF9HUEdfMjAxNl9Qb2x5Z29uc19yZWNvbnN0cnVjdGVkXzE1NS4wME1hLmdtdCIKICApICU+JQogIHRpZHkoKQoKZ2dwbG90KCkgKwogIGdlb21fbWFwKAogICAgZGF0YSA9IGtpbW1lcmlkZ2lhbl9wb2x5Z29ucywKICAgIG1hcCA9IGtpbW1lcmlkZ2lhbl9wb2x5Z29ucywKICAgIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgbWFwX2lkID0gaWQpLAogICAgZmlsbCA9ICIjRDhEOEQ4IgogICkgKwogIGdlb21fbWFwKAogICAgZGF0YSA9IGtpbW1lcmlkZ2lhbl9jb2FzdGxpbmVzLAogICAgbWFwID0ga2ltbWVyaWRnaWFuX2NvYXN0bGluZXMsCiAgICBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIG1hcF9pZCA9IGlkKSwKICAgIGNvbG91ciA9ICIjMjIyMjIyIiwgZmlsbCA9IE5BLCBzaXplID0gMC4zCiAgKSArCiAgY29vcmRfbWFwKCJtb2xsd2VpZGUiKSArCiAgbWFwX2JvcmRlcigpICsKICB0aGVtZV9tYXAoKQpgYGAKCldoZW4gcGxvdHRpbmcsIHRoZSBkYXRhIGFyZSBhZGRlZCBhcyBsYXllcnMsIGVhY2ggcGxvdHRpbmcgb3ZlciB0aGUgb3RoZXI6IHBsYXRlIHBvbHlnb25zIGF0IHRoZSBib3R0b20gdGhlbiBjb2FzdGxpbmVzIG9uIHRvcC4gSSd2ZSBhbHNvIHNldCB0aGUgbWFwIHByb2plY3Rpb24gdG8gYmUgW01vbGx3ZWlkZV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTW9sbHdlaWRlX3Byb2plY3Rpb24pLCBidXQgeW91IGNhbiBwbGF5IHdpdGggdGhpcyBhbmQgY2hhbmdlIGl0IHRvIG90aGVycyBpbmNsdWRlZCBpbiB0aGUgX21hcHByb2pfIHBhY2thZ2UgZnVuY3Rpb24gW2BtYXBwcm9qZWN0YF0oaHR0cHM6Ly9yZHJyLmlvL2NyYW4vbWFwcHJvai9tYW4vbWFwcHJvamVjdC5odG1sKS4gT3B0aW9ucyBpbmNsdWRlIGAiR2lsYmVydCJgLCBgImZpc2hleWUiYCwgYW5kIGAiaGV4ImAuIChVc2UsIGZvciBleGFtcGxlLCBgY29vcmRfbWFwKCJmaXNoZXllIiwgbiA9IDAuNylgIHRvIGFkZCBleHRyYSBhcmd1bWVudHMuKQoKQWRkaXRpb25hbGx5IEkndmUgYWRkZWQgYSBib3JkZXIgKGBtYXBfYm9yZGVyKClgLCBpbiB0aGUgYGZ1bmN0aW9uc2AgZm9sZGVyKSBhbmQgdXNlZCBhIGJhc2ljIHRoZW1lIChgdGhlbWVfbWFwKClgKSB0byBnZXQgYSBwbGFpbiBiYWNrZ3JvdW5kLgoKIyMgSG93IFIgcGxvdHMgdGhlc2UgbWFwIGRhdGEgIyMKClRoZSBwb3NpdGlvbnMgYW5kIGV4dGVudCBvZiBtYXAgYXJlYXMgZnJvbSBkYXRhIGluIEdQbGF0ZXMgYXJlIHN0b3JlZCBhcyBhIHNlcmllcyBvZiBfcG9seWdvbnMuXyBFYWNoIHJlZ2lvbiBpcyBvdXRsaW5lZCBieSBhIHNlcmllcyBvZiBwb2ludHMgdGhhdCBmb3JtIGEgY2xvc2VkIHNoYXBlLCBmaWxsZWQgd2l0aCBhIHBhcnRpY3VsYXIgY29sb3VyLiBUaGlzIGlzIGV4cG9ydGVkIGZyb20gR1BsYXRlcyBhcyBPR1ItR01UIGZvcm1hdCwgd2l0aCBmaWxlIGV4dGVuc2lvbiBgLmdtdGAsIHdoaWNoIGxpc3RzIGVhY2ggcG9pbnQgdGhhdCBtYXJrcyB0aGUgY29ybmVyIG9mIGEgcG9seWdvbi4gSW4gUiwgX2Jyb29tXyBjb252ZXJ0cyB0aGlzIHRvIGEgdGliYmxlIChzaW1pbGFyIHRvIGEgZGF0YS5mcmFtZSkgdGhhdCBoYXMgdGhlIF94XyBhbmQgX3lfIGxvY2F0aW9ucyBvZiBlYWNoIHBvaW50LCBhbmQgYSBzZXJpZXMgb2YgY29sdW1ucyB0aGF0IGlkZW50aWZ5IHdoaWNoIHBvbHlnb24gdGhhdCBwb2ludCBiZWxvbmdzIHRvLiBUaGVzZSBJRCBjb2x1bW5zIGFyZSBpbXBvcnRhbnQgdG8gbWFrZSBzdXJlIHRoYXQgcG9seWdvbnMgY29udGFpbiB0aGUgY29ycmVjdCBwb2ludHMgYW5kIGFyZSBjbG9zZWQgcHJvcGVybHkuIFdpdGhvdXQgdGhlc2UsIHRoZXJlIHdvdWxkIG9ubHkgYmUgYSBzaW5nbGUgc2hhcGUgdGhhdCB3b3VsZCBzdHJldGNoIGFjcm9zcyB0aGUgd2hvbGUgZ2xvYmUgYW5kIG5vdCBtYWtlIG11Y2ggc2Vuc2UuCgojIyBOb3QgcXVpdGUgcmlnaHQgIyMKCkdXUyBpcyBjb252ZW5pZW50IGZvciBkYXRhIGFuZCBhY2Nlc3MsIGJ1dCB0aGUgcmVzdWx0cyBpbiB0aGUgcGxvdCBhYm92ZSBzaG93IHRoZSBjdXJyZW50IGNvbnRpbmVudGFsIGNvYXN0bGluZXMgYW5kIHRoZSBwbGF0ZXMuIFRoaXMgZG9lcyBub3QgcmVmbGVjdCB0aGUgYW5jaWVudCBjb2FzdGxpbmVzIHRoYXQgd2VyZSByZWFsbHkgYWZ0ZXIuIEFsc28sIG1hbnkgb2YgdGhlIGNvdW50cmllcyBhcmUgY3Jvc3MtY3V0IGJ5IHRoZSBwb2x5Z29ucyB0aGF0IGZvcm0gdGhlIHNlY3Rpb25zIGluIHRoZSBtb2RlbCwgd2hpY2ggYXJlIHVudGlkeSBhbmQgdHlwaWNhbGx5IG5vdCB2aXNpYmxlLgoKV2UgbmVlZCB0byBnbyB0byB0aGUgbmV4dCBsZXZlbDogX3RydWUgcGFsYWVvZ2VvZ3JhcGhpY2FsIHJlY29uc3RydWN0aW9ucy5fIEZvcnR1bmF0ZWx5IEdQbGF0ZXMgb2ZmZXJzIHRoYXQgdG9vLgoKIyMgRXhlcmNpc2VzIDIgIyMKCjEuIEhhdmUgYSBsb29rIGF0IHRoZSBbR1dTIGRvY3VtZW50YXRpb25dKDxodHRwczovL2dpdGh1Yi5jb20vR1BsYXRlcy9ncGxhdGVzX3dlYl9zZXJ2aWNlX2RvYy93aWtpPikuIFdoYXQgYWRkcmVzcyB3b3VsZCB5b3UgdXNlIHRvIGdldCByZWNvbnN0cnVjdGVkIGNvYXN0bGluZXMgaW4gdGhlIGVhcmx5IENhcmJvbmlmZXJvdXMgdXNpbmcgdGhlIEBNYXR0aGV3czIwMTZHUEMgbW9kZWw/CjIuIE9wZW4gYW4gZXhhbXBsZSBvZiB0aGUgT0dSLUdNVCBkYXRhIGZyb20gdGhlIEdXUywgZWl0aGVyIGZyb20gdGhlIGBkYXRhL0dXU2AgZm9sZGVyIG9yIGJ5IGRvd25sb2FkaW5nIGl0LiBDYW4geW91IHNlZSBob3cgdGhlIHBvbHlnb25zIGFyZSBzdG9yZWQ/IEhvdyBhcmUgZGlmZmVyZW50IHBvbHlnb25zIGlkZW50aWZpZWQ/CjMuIE5vdyBsb29rIGF0IHRoZSB0aWRpZWQgdmVyc2lvbiBpbiBSLCBpLmUuIGBraW1tZXJpZGdpYW5fY29hc3RsaW5lc2Agb3IgYGtpbW1lcmlkZ2lhbl9wb2x5Z29uc2AuIEJ5IGRlZmF1bHQgdGhpcyB3aWxsIG9ubHkgc2hvdyB0aGUgZmlyc3QgMTAgcm93cywgYnV0IHlvdSBjYW4gc2VlIG1vcmUgd2l0aCwgZm9yIGV4YW1wbGUsIGBoZWFkKGtpbW1lcmlkZ2lhbl9wb2x5Z29ucywgbiA9IDUwTClgLiBDYW4geW91IHNlZSBob3cgdGhlIE9HUi1HTVQgZGF0YSBpcyB0cmFuc2ZlcnJlZCB0byB0aGlzIHRpZHkgZm9ybWF0Pwo0LiBQbGF5IGFyb3VuZCB3aXRoIHRoZSBpbnB1dHMgdG8gdGhlIHBsb3QgYWJvdmUuIEhvdyBtZXNzeSBvciBjb2xvdXJmdWwgY2FuIHlvdSBtYWtlIHRoaXMgbWFwPyBXaGF0IG9wdGlvbnMgYXJlIHRoZXJlIGluIGB0aGVtZV9tYXAoKWA/IChIaW50OiB1c2UgYD90aGVtZV9tYXBgIHRvIHNlZS4pCgoKIyAnVHJ1ZScgUGFsYWVvZ2VvZ3JhcGhpY2FsIE91dGxpbmVzICMKCkFtb25nIHRoZSBwcm9qZWN0cyBpbmNsdWRlZCB3aXRoIHRoZSBHUGxhdGVzIGRvd25sb2FkIGlzIHRoZSBzZXQgb2YgcGFsYWVvZ2VvZ3JhcGhpY2FsIHJlY29uc3RydWN0aW9ucyBieSBAQ2FvMjAxN0JiIGZvciB0aGUgbGFzdCA0MDIgbXkuIFRoZXNlIGFyZSBiYXNlZCwgYW5kIHNvIHNob3VsZCBhbGlnbiBuaWNlbHksIHdpdGggdGhlIEBHb2xvbmthMjAwN0cgbW9kZWwgdGhhdCBpcyB1c2VkIGZvciBkYXRhIGluIHRoZSBQQkRCLCB3aGljaCB3ZSdsbCBnZXQgdG8gYmVsb3cuIFRoZXNlIGRhdGEgYXJlIGluIGBHUGxhdGVzU2FtcGxlRGF0YS9GZWF0dXJlQ29sbGVjdGlvbnMvUGFsYWVvZ2VvZ3JhcGh5L0dsb2JhbC9gLgoKQENhbzIwMTdCYiB1c2VkIGEgY29tYmluYXRpb24gb2YgcHJldmlvdXMgcGFsYWVvZ2VvZ3JhcGhpY2FsIHJlY29uc3RydWN0aW9ucywgYW5kIHVwZGF0ZWQgdGhlIGNvYXN0bGluZXMsIG1vdW50YWlucywgYW5kIGV4dGVudCBvZiBsYW5kIGFuZCBpY2UgY2FwcyB1c2luZyBvY2N1cnJlbmNlIGRhdGEgZnJvbSB0aGUgUEJEQi4gSW4gYSBmZXcgbG9jYXRpb25zLCB3aGF0IGhhZCBiZWVuIHJlY29uc3RydWN0ZWQgYXMgbGFuZCBwcm9kdWNlZCBtYXJpbmUgZm9zc2lscywgd2hlbiBhY2NvdW50aW5nIGZvciB0aGUgbW92ZW1lbnQgb2YgcGxhdGVzLCBhbmQgdmljZSB2ZXJzYS4KClRoZXNlIHJlY29uc3RydWN0aW9ucyBpbmNsdWRlIGZvdXIgbGF5ZXJzIHRoYXQgaGF2ZSB0aGUgc2VwYXJhdGVkIHBhbGFlb2V2b252aXJvbm1lbnRhbCByZWdpb25zOgoKKiBTaGFsbG93IG1hcmluZTogc2hlbGYgc2Vhcy4KKiBMYW5kOiB0ZXJyZXN0cmlhbCBoYWJpdGF0cyBvdXRsaW5lZCBieSBjb2FzdGxpbmVzLgoqIE1vdW50YWluOiBhcmVhcyBvZiBtb3VudGFpbiBidWlsZGluZyBhbmQgdXBsaWZ0LgoqIEljZSBjYXA6IHBvbGFyIHJlZ2lvbnMgdGhhdCBoYXZlIHN1YnN0YW50aWFsIGV2aWRlbmNlIGZvciBsb25nIHRlcm0gcHJlc2VuY2Ugb2YgaWNlLgoKVGhlc2UgYXJlIGdpdmVuIHRoZSBhYmJyZXZpYXRpb25zICdzbScsICdsJywgJ20nIGFuZCAnaScuIEl0J3Mgd29ydGggbm90aW5nIHRoYXQgdGhlIHdheSB0aGVzZSBhcmUgYnVpbHQgbWVhbnMgdGhhdCB0aGUgbGF5ZXJzIGhhdmUgdG8gYmUgcGxvdHRlZCBpbiB0aGUgb3JkZXIgYWJvdmUg4oCTIHNoYWxsb3cgbWFyaW5lIGZpcnN0LCBpY2UgY2FwIGxhc3QuIFRoZXJlIGFyZSB2YXJpb3VzIGFyZWFzIHRoYXQgb3ZlcmxhcCAoQXVzdHJhbGlhIGVzcGVjaWFsbHkpIHNvIHRoZSBvcmRlciBvZiBvdmVybGF5IGJlY29tZXMgaW1wb3J0YW50IGluIG1ha2luZyB0aGUgY29ycmVjdCBib3VuZGFyaWVzIHZpc2libGUuCgojIyBFeHBvcnQgZnJvbSBHUGxhdGVzICMjCgpUaGUgQENhbzIwMTdCYiByZWNvbnN0cnVjdGlvbnMgYXJlbid0IGF2YWlsYWJsZSBmcm9tIEdXUywgYnV0IGNhbiBiZSBleHBvcnRlZCBmcm9tIEdQbGF0ZXMgZGlyZWN0bHkgdXNpbmcgdGhlIG1lbnUgaXRlbSBfUmVjb25zdHJ1Y3Rpb24gPiBFeHBvcnTigKZfIExpa2UgYWJvdmUgSSd2ZSBhbHJlYWR5IGRvbmUgdGhpcyBmb3IgYW4gZXhhbXBsZSBkYXRhIHNldCwgaW4gdGhpcyBjYXNlIHVzaW5nIHRoZSBMYXRlIEp1cmFzc2ljIChLaW1tZXJpZGdpYW4sIDE1NSBNYSkgYXMgYW4gZXhhbXBsZS4gVG8gbWFrZSB5b3VyIG93biwgb25jZSBpbiB0aGUgZXhwb3J0IHdpbmRvdzoKCjEuIFNlbGVjdCB0aGUgdGltZSBvZiBleHBvcnQgKGNhbiAgYmUgc2V0IGZyb20gdGhlIG1haW4gd2luZG93IHRpbWUpLgogICAgLSBBbHNvIGFuIG9wdGlvbiB0byBleHBvcnQgYSBzZXJpZXMgYXQgcmVndWxhciBpbnRlcnZhbHMuCjIuIFRoZSBsYXllcnMgdG8gZXhwb3J0CiAgICAxLiBzZWxlY3QgX0FkZCBFeHBvcnTigKZfIHVuZGVyIEV4cG9ydCBEYXRhCiAgICAyLiBjaG9vc2UgX1JlY29uc3RydWN0ZWQgR2VvbWV0cmllc18KICAgIDMuIG91dHB1dCBhcyBfT0dSLUdNVF8KICAgIDQuIGNob29zZSBzaW5nbGUgb3IgbXVsdGlwbGUgZmlsZXMKICAgICAgICArIEkgcHJlZmVyIG11bHRpcGxlOiB5b3UgZ2V0IG9uZSBmaWxlIGZvciBlYWNoIG9mIHNlYS9sYW5kL21vdW50YWluL2ljZS4gVGhpcyBpcyB0aGUgZm9ybSB0aGF0IHdvcmtzIHdpdGggdGhlIGNvZGUgYmVsb3cuCiAgICA1LiBfT0tfCjMuIENoYW5nZSBfVGFyZ2V0IGRpcmVjdG9yeTpfIHRvIHNvbWV3aGVyZSB5b3UgY2FuIGZpbmQuCjMuIF9CZWdpbiBFeHBvcnRfCgohW0V4cG9ydCBvcHRpb25zIGZyb20gR1BsYXRlcy5dKC4vZmlncy9ncGxhdGVzX2V4cG9ydDEucG5nKQoKIVtMYXllciBzZWxlY3Rpb24gaW4gR1BsYXRlcyBleHBvcnQuIFNlbGVjdCBSZWNvbnN0cnVjdGVkIGdlb21ldHJpZXMsIE9HUi1HTVQsIGFuZCBtdWx0aXBsZSBsYXllcnMuXSguL2ZpZ3MvZ3BsYXRlc19leHBvcnQyLnBuZykKClRoZSBleGFtcGxlIGRhdGEgYXJlIGluIGBkYXRhL3BhbGFlb2dlb2dyYXBoeWAgYXMgdGhyZWUgYC5nbXRgIGZpbGVzIHRoYXQgZGVzY3JpYmUgdGhlIHBvbHlnb24gb3V0bGluZXMgZm9yIGVhY2ggb2YgdGhlIGxheWVycy4gTm90ZSB0aGF0IGFzIHRoZXJlIGFyZSBubyBpY2UgY2FwcyBhdCB0aGlzIHRpbWUsIHRoZXJlIGFyZSBvbmx5IHRocmVlIGZpbGVzL2xheWVycy4gTG9vayBhdCB0aGUgQ2FyYm9uaWZlcm91cyBvZiBsYXRlciBQYWxhZW9nZW5lIHRvIGdldCBzb21lIGljZSBjYXBzLgoKIyMgUmVhZCBpbiB0aGUgbWFwIGRhdGEgIyMKCkkgZmluZCBpdCB1c2VmdWwgdG8gdGllIHRvZ2V0aGVyIHRoZSBsYXllcnMgYW5kIGNvbG91cnMgZnJvbSB0aGUgYmVnaW5uaW5nIG9mIGltcG9ydGluZyDigJMgdGhpcyBlbnN1cmVzIGtlZXBpbmcgdGhlIGxheWVycyBpbiB0aGUgb3JkZXIgZm9yIHBsb3R0aW5nLiBUaGUgZGF0YSBmaWxlcyBhcmU6CgoqIGBsbV80MDJfMl9yZWNvbnN0cnVjdGVkXzE1NS4wME1hLmdtdGA6IGxhbmRtYXNzCiogYG1fNDAyXzJfcmVjb25zdHJ1Y3RlZF8xNTUuMDBNYS5nbXRgOiBtb3VudGFpbgoqIGBzbV80MDJfMl9yZWNvbnN0cnVjdGVkXzE1NS4wME1hLmdtdGA6IHNoYWxsb3cgbWFyaW5lCgpUaGlzIHNob3J0IGNvZGUgc25pcHBldCBzZXRzIGxpc3RzIHRoZSBsYXllciBuYW1lcyBhbmQgYXNzaWducyB0aGUgY29sb3VycyBmb3IgZWFjaCBsYXllciBvbiB0aGUgbWFwIOKAkyB0aGV5IGFyZSBzaW1pbGFyIHRvIHRob3NlIHNob3duIGluIEdQbGF0ZXMsIGJ1dCBub3QgcXVpdGUgdGhlIHNhbWUuIEkndmUgYWxzbyB1c2VkIGAjREFEM0ZGYCBmb3IgaWNlIGNhcHMgd2hlbiBuZWVkZWQuIChUZWNobmljYWxseSB0aGlzIGlzbid0IHVzZWQgZm9yIHRoZSBjb2xvdXJzLCB0aGF0J3MgaW4gdGhlIGBwYWxhZW9nZW9nX21hcF9uaWNldGllc2AgZnVuY3Rpb24sIHRoaXMgaXMgc29tZXdoYXQgYSByZW1uYW50IG9mIGEgcHJldmlvdXMgd2F5IHRoYXQgSSBtYWRlIHRoaXMuKQoKYGBge3IgbWFwX2xheWVyc30KbWFwX2xheWVycyA8LQogIGMoCiAgICAiTGFuZCIgICAgICAgICAgID0gIiNGRkQyM0EiLAogICAgIk1vdW50YWluIiAgICAgICA9ICIjRkY4RDUxIiwKICAgICJTaGFsbG93IG1hcmluZSIgPSAiIzQ1RDhGRiIKICApCmBgYAoKV2l0aCB0aGF0IHJlYWR5LCB0aGUgZm9sbG93aW5nIGNvZGUgcmVhZHMgYW5kIHRpZGllcyB0aGUgZGF0YSAoYXMgZm9yIGFib3ZlKSwgdGhlbiBhZGRzIGEgY29sdW1uIChnZW9nX2xheWVyKSB0byBpZGVudGlmeSB0aGUgZGF0YSBsYXllciAoc2hhbGxvdyBtYXJpbmUsIGxhbmQsIG1vdW50YWluKSBhbmQgam9pbnMgYWxsIHRoZSBkYXRhIHRvZ2V0aGVyLiBJdCdzIHZlcnkgX3RpZHl2ZXJzZV8gYmFzZWQsIHNvIEknZCBzdWdnZXN0IGxvb2tpbmcgYXQgPGh0dHBzOi8vd3d3LnRpZHl2ZXJzZS5vcmc+IHRvIHNlZSB0aGUgcmF0aW9uYWxlIGFuZCBob3cgdGhleSBsaW5rIHVzaW5nIHRoZSBfcGlwZV8gKGAlPiVgKS4gWW91IGNhbiBhbHNvIHNlZSBtb3JlIGFib3V0IGluZGl2aWR1YWwgZnVuY3Rpb25zIGluIHRoZSBSIGhlbHAuCgpUaGUgZmluYWwgc3RlcCBpcyB0byBjcmVhdGUgYSBjb2x1bW4gKHBvbHlnb25faWQpIHRvIGlkZW50aWZ5IGVhY2ggcmVnaW9uIG9uIHRoZSwgd2hpY2ggY29tZSBhZnRlciBtb3VudGFpbiBtYXAuIEkgaGFkIGEgbG9uZyBpc3N1ZSB3aGVyZSBkaWZmZXJlbnQgY29udGluZW50cyB3b3VsZCBqb2luIHVwLCBvciBBdXN0cmFsaWEncyBsYW5kIHdvdWxkIGFsd2F5cyBhcHBlYXIgcGxvdHRlZCB1bmRlcm5lYXRoIHRoZSBzZWEuIEFzIGRlc2NyaWJlZCBhYm92ZSwgdGhpcyBpcyBiZWNhdXNlIHRoZSBzaGFwZXMgb24gdGhlIG1hcCBhcmUgcGxvdHRlZCBhcyBzZXBhcmF0ZSBwb2x5Z29ucywgYW5kIHRoZSBJRHMgYXJlIHJlcGVhdGVkIGJldHdlZW4gZWFjaCBvZiB0aGUgbGF5ZXJzLiBXZSdsbCB1c2UgdGhlIHBvbHlnb25faWQgY29sdW1uIHRvIG1ha2Ugc3VyZSB0aGF0IGVhY2ggcG9seWdvbiBoYXMgYSB1bmlxdWUgSUQgYnkgam9pbmluZyB0aGUgbGF5ZXIsIGdyb3VwIGFuZCBpZCBuYW1lIGZyb20gdGhlIHRpZGllZCBkYXRhLgoKYGBge3IgcmVhZF9wb2x5Z29uX2RhdGF9CnBvbHlnb25fZGF0YSA8LQogIGxpc3QuZmlsZXMoImRhdGEvcGFsYWVvZ2VvZ3JhcGh5LyIsIHBhdHRlcm4gPSAiLmdtdCIsIGZ1bGwubmFtZXMgPSBUUlVFKSAlPiUKICAgIG1hcChyZWFkT0dSKSAlPiUKICAgIG1hcCh0aWR5KSAlPiUKICAgIG1hcDIoCiAgICAgIG5hbWVzKG1hcF9sYXllcnMpLAogICAgICB+IGFkZF9jb2x1bW4oLngsIGdlb2dfbGF5ZXIgPSAueSkKICAgICkgJT4lCiAgICBiaW5kX3Jvd3MoKSAlPiUKICAgIG11dGF0ZSgKICAgICAgcG9seWdvbl9pZCA9IHN0cl9jKGlkLCBncm91cCwgZ2VvZ19sYXllcikgJT4lIGFzX2ZhY3RvcigpLAogICAgICBnZW9nX2xheWVyID0gZmFjdG9yKGdlb2dfbGF5ZXIsIGxhYmVscyA9IG5hbWVzKG1hcF9sYXllcnMpKQogICAgKQpgYGAKCiMjIEFycmFuZ2luZyB0aGUgbWFwIGxheWVycyAjIwoKQXQgdGhpcyBwb2ludCBpdCdzIGltcG9ydGFudCB0byBoYXZlIHRoZSBtYXAgbGF5ZXJzIGluIHRoZSBjb3JyZWN0IG9yZGVyLCBzbyB0aGF0IG1hcmluZSBpcyBwbG90dGVkIHVuZGVyIGxhbmQsIHdoaWNoIGlzIHBsb3R0ZWQgdW5kZXIgbW91bnRhaW4uCgpUaGlzIHBpZWNlIG9mIGNvZGUgZG9lcyB0aGF0IGZvciB5b3UsIGVuc3VyaW5nIHNoYWxsb3cgbWFyaW5lIGlzIGZpcnN0IChpdCB3YXMgdGhpcmQgaW4gYG1hcF9sYXllcnNgIGFib3ZlKSB0aGVuIGFzc2lnbmluZyB0aGlzIG9yZGVyIHRvIHRoZSBwb2x5Z29uX2lkIGNvbHVtbi4gSXQgY29udmVydHMgdGhlIHBvbHlnb25faWQgY29sdW1uIHRvIGEgZmFjdG9yIGFuZCByZS1vcmRlcnMgdGhlZSBsZXZlbHMgYmFzZWQgb24gdGhlIHJlb3JkZXJlZCBgbWFwX2xheWVyc2AuCgpgYGB7ciByZWFycmFuZ2VfcG9seWdvbl9sYWJlbHN9CmlkX2xldmVsX29yZGVyIDwtCiAgbWFwX2xheWVyc1tjKDMsIDEsIDIpXSAlPiUKICAgIG1hcCgKICAgICAgZnVuY3Rpb24obGF5cikgcG9seWdvbl9kYXRhJHBvbHlnb25faWQgJT4lIGxldmVscygpICU+JSBzdHJfd2hpY2gobGF5cikKICAgICkgJT4lCiAgICB1bmxpc3QoKQoKcG9seWdvbl9kYXRhIDwtCiAgcG9seWdvbl9kYXRhICU+JQogICAgbXV0YXRlKAogICAgICBwb2x5Z29uX2lkID0gZmN0X3JlbGV2ZWwocG9seWdvbl9pZCwgbGV2ZWxzKHBvbHlnb25faWQpW2lkX2xldmVsX29yZGVyXSkKICAgICkKYGBgCgojIyBQbG90dGluZyB0aGUgbWFwICMjCgpVbmZvcnR1bmF0ZWx5LCB1c2luZyB0aGUgc2FtZSBwbG90IGxheWVyIG1ldGhvZCBhcyBmb3IgdGhlIEdXUyBkYXRhIGFib3ZlIGlzIGEgbGl0dGxlIGV4Y2Vzc2l2ZS4gWW91IHdvdWxkIGhhdmUgdG8gYWRkIGEgbmV3IGFubm90YXRpb24gZm9yIGVhY2ggbGF5ZXIgKGp1c3QgYWJvdXQgb2theSBmb3IgdGhyZWUsIHRlZGlvdXMgZm9yIG1vcmUpLCB3aGljaCB0aGVuIG11bHRpcGxpZXMgd2hlbiBwbG90dGluZyByZWNvbnN0cnVjdGlvbnMgb2YgbWFueSB0aW1lcy4KCmBgYHtyIG1hbnlfYW5ub3RhdGlvbnMsIGV2YWwgPSBGQUxTRX0KIyBETyBOT1QgUlVOIFRISVMgQ09ERQpnZ3Bsb3QoKSArCiAgZ2VvbV9tYXAoKSArCiAgZ2VvbV9tYXAoKSArCiAgZ2VvbV9tYXAoKSArIOKApgpgYGAKCkluc3RlYWQsIHlvdSBjYW4gdXNlIHRoZSBmdW5jdGlvbiBgZ2VvbV9wb2x5Z29uYCBhbmQgaGF2ZSB0aGUgZGlmZmVyZW50IGxheWVycyBwbG90dGVkIGF1dG9tYXRpY2FsbHkgd2l0aCB0aGUgY29sb3VycyBhc3NpZ25lZCBhYm92ZS4gVGhlIF9maWxsXyBpcyB0aGUgbW9zdCBpbXBvcnRhbnQgYXJndW1lbnQgYXMgdGhhdCBhc3NpZ25zIHRoZSBtYWluIGJ1bGsgb2YgdGhlIGNvbG91ci4gVGhlIF9jb2xvdXJfIGFyZ3VtZW50IGlzIHRoZSBvdXRsaW5lLCB3aGljaCB5b3Ugc2hvdWxkbid0IHVzZSBiYXMgdGhhdCB3aWxsIG91dGxpbmUgdGhlIG1hcCBsYXllcnMgYW5kIGNoYW5nZSB0aGVpciBzaGFwZS4gVGhlIGBjb2xvdXJgIGFyZ3VtZW50IHdpbGwgYmUgdXNlZCB3aGVuIHBsb3R0aW5nIG9jY3VycmVuY2UgcG9pbnRzIChzZWUgYmVsb3cpLiBBbHNvIGhhdmluZyB0aGUgb3JkZXJlZCBhbmQgY29ycmVjdGx5IGdyb3VwZWQgX3BvbHlnb25faWRfIGlzIGltcG9ydGFudCBoZXJlIGFzIHRoYXQgZGV0ZXJtaW5lcyBob3cgdGhlIGluZGl2aWR1YWwgcG9seWdvbnMgYXJlIGdyb3VwZWQgYW5kIGNsb3NlZCwgcHJldmVudGluZyBzaGFwZXMgZnJvbSBzcGFubmluZyB0aGUgZ2xvYmUsIG9yIGluY29ycmVjdGx5IG92ZXJsYXBwaW5nLgoKSSdsbCBwb2ludCBvdXQgaGVyZSB0aGF0IHRoZXJlJ3MgcXVpdGUgYSBsb3QgZ29pbmcgb24gaW4gdGhlIGBwYWxhZW9nZW9nX21hcF9uaWNldGllc2AgZnVuY3Rpb24gYXQgdGhlIGVuZC4gVGhpcyBpcyBhIGNvbGxlY3Rpb24gb2YgdGhlbWUgb3B0aW9ucyBhbmQgYSBjb2xvdXIgc2NhbGUgdG8gc2hvdyB0aGUgbWFwIHByb3Blcmx5LiBJbiBwYXJ0aWN1bGFyIHRoaXMgYWRkcyB0aGUgb3V0bGluZSBhbmQgdGhlIGxpbmVzIG9mIHRoZSBlcXVhdG9yIGFuZCB0cm9waWNzLiBJIGd1ZXNzIG9uZSB0aGluZyB0byBub3RlIGlzIHRoYXQgdGhlICd0cm9waWNzJywgaW4gYSBiaW9kaXZlcnNpdHkgc2Vuc2UsIG1heSBub3QgYmUgdGhlIHNhbWUgaW4gdGhlIHBhc3QgYXMgdGhlIHByZXNlbnQuIEluY3JlYXNlZCB0ZW1wZXJhdHVyZXMgY2FuIGNoYW5nZSB0aGUgbGF0aXR1ZGluYWwgYmlvZGl2ZXJzaXR5IGdyYWRpZW50LiBBbHNvLCBvYmxpcXVpdHkgYW5kIHByZWNlc3Npb24gY2hhbmdlIHRoZSBsYXRpdHVkZXMgb2YgdGhlIGFzdHJvbm9taWNhbCB0cm9waWNzIHRocm91Z2ggdGltZS4KCmBgYHtyIHBsb3RfYmFzZV9tYXAsIGZpZy53aWR0aCA9IDcsIGZpZy5oZWlnaHQgPSA0LCBmaWcuY2FwID0gIioqUGFsYWVvZ2VvZ3JhcGh5IGluIHRoZSBLaW1tZXJpZGdpYW4gKDE1NSBNYSkuKiogUGFsYWVvZ2VvZ3JhcGhpY2FsIHJlY29uc3RydWN0aW9uIGZyb20gQENhbzIwMTdCYi4gTGluZXMgb2YgbGF0aXR1ZGUgaW5kaWNhdGUgdGhlIGxvY2F0aW9ucyBvZiB0aGUgRXF1YXRvciBhbmQgVHJvcGljcyAowrEyMy41wrApLiJ9CiMgZGV2Lm5ldyh3aWR0aCA9IDcsIGhlaWdodCA9IDQpCm1hcF9wbG90IDwtCiAgZ2dwbG90KCkgKwogICAgZ2VvbV9wb2x5Z29uKAogICAgICBkYXRhID0gcG9seWdvbl9kYXRhLAogICAgICBhZXMoCiAgICAgICAgeCAgICAgID0gbG9uZywKICAgICAgICB5ICAgICAgPSBsYXQsCiAgICAgICAgZmlsbCAgID0gZ2VvZ19sYXllciwKICAgICAgICBncm91cCAgPSBwb2x5Z29uX2lkCiAgICAgICksCiAgICAgIGNvbG91ciA9IE5BCiAgICApICsKICAgIGNvb3JkX21hcCgibW9sbHdlaWRlIikgKwogICAgdGhlbWVfbWFwKCkgKwogICAgcGFsYWVvZ2VvZ19tYXBfbmljZXRpZXMoKQptYXBfcGxvdApgYGAKCllvdSBjYW4gdHJ5IGNoYW5naW5nIHRoZSBsb29rIGJ5IG1vZGlmeWluZyB0aGUgcHJvamVjdGlvbiAoYGNvb3JkX21hcGApIGFuZCBwbGF5IGFib3V0IHdpdGggc29tZSBvZiB0aGUgY29sb3VycyBhbmQgbWFya2luZ3MgYnkgZWRpdGluZyB0aGUgZnVuY3Rpb24gYHBhbGFlb2dlb2dfbWFwX25pY2V0aWVzYCAoYGZ1bmN0aW9ucy9wYWxhZW9nZW9nX21hcF9uaWNldGllcy5SYCkuIFlvdSBjYW4gYWxzbyBhZGQgZnVydGhlciBhbm5vdGF0aW9ucyAoPGh0dHBzOi8vZ2dwbG90Mi1ib29rLm9yZy9hbm5vdGF0aW9ucy5odG1sI2Fubm90YXRpb25zPikgb3IgY2hhbmdlIGxhYmVscyBhcyB5b3Ugd2lzaC4KCiMjIEdvaW5nIGZ1cnRoZXIgMTogbG9jYXRpbmcgd2l0aCBtb2Rlcm4gY29hc3RsaW5lcyAjIwoKWW91IG1heSBhbHNvIHdhbnQgdG8gYWRkIG91dGxpbmVzIG9mIG1vZGVybiBjb2FzdGxpbmVzIHRvIHNob3cgd2hlcmUgdGhlc2UgYXJlIGFuZCBoZWxwIGxvY2F0aW5nLiBBZ2FpbiB0aGVzZSBhcmUgbm90IGF2YWlsYWJsZSBmcm9tIEdXUywgYnV0IGNhbiBiZSBleHBvcnRlZCBmcm9tIEdQbGF0ZXMuIEluIEdQbGF0ZXMsIGZyb20gX0ZpbGUgPiBPcGVuIEZlYXR1cmUgQ29sbGVjdGlvbuKApl8gZ28gdG8gYEdwbGF0ZXNTYW1wbGVEYXRhL0ZlYXR1cmVDb2xsZWN0aW9ucy9Db2FzdGxpbmVzYCBhbmQgbG9hZCBgTWF0dGhld3NfZXRhbF9HUENfMjAxNl9Db2FzdGxpbmVzX1BvbHlsaW5lLmdwbWx6YC4gVGhpcyBhZGRzIGEgbmV3IGxheWVyIGludG8gR1BsYXRlcyB3aXRoIGFyZWFzIG9mIG1vZGVybiBjb2FzdGxpbmUgaW4gdGhlaXIgcmVsYXRpdmUgcG9zaXRpb25zIGZvciB0aGUgdGltZS4gTm90IGFsbCB0aGUgbW9kZXJuIGNvYXN0bGluZXMgYXJlIHRoZXJlIGFzIG5vdCBhbGwgb2YgdGhlc2UgaGF2ZSBjZXJ0YWluIGxvY2F0aW9ucywgb3Igcm9jayB0byBiZSBmb3VuZCBhdCDigJMgdGhlc2UgY29tZSBhbmQgZ28gYXQgZGlmZmVyZW50IHRpbWUgcG9pbnRzLgoKTGlrZSBhYm92ZSwgeW91IGNhbiBleHBvcnQgdGhpcyBsYXllciBmcm9tIEdQbGF0ZXMuIEluIHRoaXMgcmVwbywgSSd2ZSBhbHJlYWR5IGRvbmUgdGhhdCB0byBgZGF0YS9jb2FzdGxpbmVzL2AsIHdoaWNoIHlvdSBjYW4gbG9hZCB3aXRoIHRoaXMgY29kZS4KCmBgYHtyIHJlYWRfY29hc3RsaW5lX3BvbHlnb25zLCByZXN1bHRzID0gImhpZGUifQptb2Rlcm5fY29hc3RsaW5lcyA8LQogIHJlYWRPR1IoImRhdGEvY29hc3RsaW5lcy9NYXR0aGV3c19ldGFsX0dQQ18yMDE2X0NvYXN0bGluZXNfUG9seWxpbmVfcmVjb25zdHJ1Y3RlZF8xNTUuMDBNYS5nbXQiKSAlPiUKICAgIHRpZHkoKSAlPiUKICAgIGFkZF9jb2x1bW4oZ2VvZ19sYXllciA9ICJNb2Rlcm4gY29hc3RsaW5lcyIpICU+JQogICAgbXV0YXRlKHBvbHlnb25faWQgPSBzdHJfYyhpZCwgZ3JvdXAsIGdlb2dfbGF5ZXIpICU+JSBhc19mYWN0b3IoKSkKYGBgCgpUaGlzIGxheWVyIG5lZWRzIHRoZSBzYW1lIG9yZ2FuaXNpbmcgb2YgSURzIGFzIHRoZSBwYWxhZW9nZW9ncmFwaHkgYWJvdmUsIGJ1dCBvbmNlIGRvbmUgY2FuIGJlIGVhc2lseSBhZGRlZCB0byBgbWFwX3Bsb3RgIGFib3ZlLiBUaGlzIGlzIHdoZXJlIGl0J3MgdXNlZnVsIHRvIHNhdmUgeW91ciBnZ3Bsb3QgdG8gYW4gb2JqZWN0IGFsb25nc2lkZSBwcmludGluZyBpdCDigJMgdGhpcyB5b3UgY2FuIGp1c3QgYWRkIG5ldyBsYXllcnMgb24gdG9wIG9mIHRoZSBiYXNlIG1hcC4KCmBgYHtyIHBsb3RfY29hc3Rfb3V0bGluZXMsIGZpZy53aWR0aCA9IDcsIGZpZy5oZWlnaHQgPSA0LCBmaWcuY2FwID0gIioqUGFsYWVvZ2VvZ3JhcGh5IGluIHRoZSBLaW1tZXJpZGdpYW4gKDE1NSBNYSkuKiogT3V0bGluZXMgb2YgbW9kZXJuIGNvYXN0bGluZXMgKHdoZXJlIGtub3duKSBhcmUgaW5jbHVkZWQgaW4gZ3JleS4gUGFsYWVvZ2VvZ3JhcGhpY2FsIHJlY29uc3RydWN0aW9uIGZyb20gQENhbzIwMTdCYi4gTGluZXMgb2YgbGF0aXR1ZGUgaW5kaWNhdGUgdGhlIGxvY2F0aW9ucyBvZiB0aGUgRXF1YXRvciBhbmQgVHJvcGljcyAowrEyMy41wrApLiJ9Cm1hcF9wbG90ICsKICBnZW9tX3BhdGgoCiAgICBkYXRhID0gbW9kZXJuX2NvYXN0bGluZXMsCiAgICBhZXMoCiAgICAgIHggPSBsb25nLAogICAgICB5ID0gbGF0LAogICAgICBncm91cCA9IHBvbHlnb25faWQsCiAgICApLAogICAgY29sb3VyID0gIiM4ODg4ODgiLCBzaXplID0gMC4zCiAgKQpgYGAKCiMjIEV4ZXJjaXNlcyAzICMjCgoxLiBUcnkgcGxvdHRpbmcgdGhpcyBtYXAgd2l0aCBkaWZmZXJlbnQgcHJvamVjdGlvbnMgKHNlZSBhYm92ZSBhbmQgYD9tYXBwcm9qZWN0YCkuIERvZXMgdGhpcyB3b3JrIHdlbGwgZm9yIGFsbCBwcm9qZWN0aW9ucz8gV2h5PwoyLiBMb29rIGF0IHRoZSBjb2RlIGJlaGluZCBgcGFsYWVvZ2VvZ19tYXBfbmljZXRpZXMoKWAgKGluIGBmdW5jdGlvbnMvcGFsYWVvZ2VvZ19tYXBfbmljZXRpZXMuUmApLiBNb2RpZnkgdGhpcyB0byBhZGQgZGlmZmVyZW50IGxpbmVzIG9mIGxhdGl0dWRlLiBDYW4geW91IGFkZCBsaW5lcyBvZiBsb25naXR1ZGUgdG9vPyBXaGF0IGFib3V0IGNoYW5naW5nIHRoZSBjb2xvdXI/CiAgLSBfZ2dwbG90XyBvYmplY3RzIGNhbiBoYXZlIG5ldyBsYXllcnMgYWRkZWQgdG8gdGhlbSBvbmUtYnktb25lIGJ5IHVzaW5nIGArYCwgYnV0IHlvdSBjYW4gYWxzbyBjb21iaW5lIHRoZW0gaW50byBmdW5jdGlvbiBjb250YWluaW5nIGEgbGlzdCBvZiBsYXllcnMgdGhhdCBjYW4gYmUgYWRkZWQgaW4gYSBzaW5nbGUgbGluZS4KICAtIEhpbnQ6IHRvIGNoYW5nZSB0aGUgY29sb3VyIG9mIHRoZSBtYXAgbGF5ZXJzLCB1c2UgYGZpbGxgLgozLiBDYW4geW91IHRyeSBjcmVhdGluZyB5b3VyIG93biBwYWxhZW9nZW9ncmFwaGljYWwgbWFwIGZvciB5b3VyIGZhdm91cml0ZSB0aW1lPyBUaGlzIHNob3VsZCBiZSBkb25lIG9uIHlvdXIgb3duIGNvbXB1dGVyLiBGb2xsb3cgdGhlIGluc3RydWN0aW9ucyBhYm92ZSB0byBleHBvcnQgZGF0YSBmcm9tIEdQbGF0ZXMgdGhlbiBwbG90IGluIFIsIG1ha2luZyBzdXJlIHlvdSBjaGFuZ2UgYWxsIHRoZSBmaWxlIGFuZCBvYmplY3QgbmFtZXMuCgoKIyBBZGRpbmcgRm9zc2lsIE9jY3VycmVuY2VzICMKCiMjIEdldHRpbmcgb2NjdXJyZW5jZSBkYXRhICMjCgpOb3cgdGhhdCB5b3UndmUgc3BlbnQgbG90cyBvZiB0aW1lIG1ha2luZyBhIHByZXR0eSBtYXAsIGxldCdzIHBsb3Qgc29tZSBmb3NzaWxzIG9uIHRvcCBvZiBpdC4KCk9mIGNvdXJzZSwgYmVpbmcgbWUgYW5kIGhhdmluZyB1c2VkIEp1cmFzc2ljIGRhdGEsIHdlJ3JlIGdvaW5nIHRvIHBsb3Qgc29tZSBpY2h0aHlvc2F1cnMuIEluIHRoaXMgaW5zdGFuY2UsIHRoZSBvY2N1cnJlbmNlIGRhdGEgY29tZXMgZnJvbSB0aGUgUEJEQiwgZG93bmxvYWRpbmcgaWNodGh5b3NhdXIgb2NjdXJyZW5jZXMgd2l0aCB0aGVzZSBmaWx0ZXJzOgoKLSBDYWxsb3ZpYW7igJNUaXRob25pYW4gKDE2Ni0xNDUgTWEpCi0gYWxsIGxldmVscyBvZiB0YXhvbm9teSDigJMgbm8gZmlsdGVyaW5nLgoKVGhlIFBCREIgaGFzIGl0cyBvd24gd2ViIEFQSSAoPGh0dHBzOi8vcGFsZW9iaW9kYi5vcmcvZGF0YTEuMj4pIGZyb20gd2hpY2ggeW91IGNhbiBnZXQgb2NjdXJyZW5jZXMsIGNvbGxlY3Rpb25zLCB0YXhvbm9teSBhbmQgb3RoZXIgdGhpbmdzLiBJbiB0aGUgY29kZSBjaHVuayBiZWxvdyBJJ3ZlIGNyYWZ0ZWQgdGhlIFVSTCB0byBkb3dubG9hZCB0aGUgZGF0YSwgYnV0IHRoaXMgaXMgYWxyZWFkeSBpbiB0aGUgcmVwbyBzbyBkb2Vzbid0IG5lZWQgdG8gYmUgcnVuLgoKKipEbyBub3QgcnVuIHRoZSBmb2xsb3dpbmcgY2h1bmssIHRoZSBkYXRhIGlzIGFscmVhZHkgaW4gdGhlIHJlcG8uKioKCmBgYHtyIGRvd25sb2FkX3BiZGJfZGF0YSwgZXZhbCA9IEZBTFNFfQojIyMjIERPIE5PVCBSVU4gVEhJUyBDSFVOSyAjIyMjCnBiZGJfdXJsIDwtCiAgImh0dHBzOi8vcGFsZW9iaW9kYi5vcmcvZGF0YTEuMi9vY2NzL2xpc3QuY3N2P2Jhc2VfbmFtZT1JY2h0aHlvc2F1cm9tb3JwaGEmaW50ZXJ2YWw9Q2FsbG92aWFuLE94Zm9yZGlhbixLaW1tZXJpZGdpYW4sVGl0aG9uaWFuJnNob3c9cGFsZW9sb2MiCgpvY2NfaWNodGh5b3NhdXJzIDwtCiAgcmVhZF9jc3YocGJkYl91cmwpCmBgYAoKVGhlIHBhcnQgb2YgdGhlIFVSTCBgc2hvdz1wYWxlb2xvY2AgaXMgdGhlIGltcG9ydGFudCBiaXQgYXMgdGhlIFBCREIgaGFzIGFscmVhZHkgY29tcHV0ZWQgdGhlIHBhbGFlb2Nvb3JkaW5hdGVzIG9mIG1vc3Qgb2NjdXJyZW5jZXMgdXNpbmcgdGhlaXIgbW9kZXJuIGxvY2F0aW9uIGFuZCB0aGUgYWdlcyBvZiB0aGUgb2NjdXJyZW5jZXMuIENvbnZlbmllbnRseSB1c2luZyB0aGUgc2FtZSBkZWZhdWx0IEdQbGF0ZXMgbW9kZWwgYXMgdGhlIHBhbGFlb2dlb2dyYXBoeS4KCiMjIFBsb3R0aW5nIG9jY3VycmVuY2VzICMjCgpXaXRoIHRoYXQgYWxsIGRvbmUsIHVzZSB0aGUgY2h1bmsgYmVsb3cgdG8gYWRkIHRoZSBvY2N1cnJlbmNlcyBvbnRvIHRoZSBiYXNlIG1hcC4gVGhlc2UgYXJlIHBsb3R0ZWQgd2l0aCBgZ2VvbV9wb2ludCgpYCwgd2hpY2ggeW91IG1heSBiZSBmYW1pbGlhciB3aXRoLCBzZXR0aW5nIHRoZSBfeF8gYW5kIF95XyB2YWx1ZXMgdG8gdGhlIHBhbGFlb2xvbmdpdHVkZSBhbmQgcGFsYWVvbGF0aXR1ZGUgcmVzcGVjdGl2ZWx5LgoKYGBge3IgcGxvdF9vY2N1cnJlbmNlcywgZmlnLmhlaWdodCA9IDQsIGZpZy53aWR0aCA9IDcsIGZpZy5jYXAgPSAiKipPY2N1cnJlbmNlcyBvZiBpY2h0aHlvc2F1cnMgZnJvbSB0aGUgQ2FsbG92aWFu4oCTVGl0aG9uaWFuLioqIFBhbGFlb2dlb2dyYXBoaWNhbCBtYXAgc2hvd3MgdGhlIGRpc3RyaWJ1dGlvbiBvZiBsYW5kIGluIHRoZSBLaW1tZXJpZGdpYW4gKDE1NSBNYSkgcmVjb25zdHJ1Y3RlZCBieSBAQ2FvMjAxN0JiLiBPY2N1cnJlbmNlcyBvZiBJY2h0aHlvc2F1cm9tb3JwaGEgYXJlIGZyb20gdGhlIFBhbGFlb2Jpb2xvZ3kgRGF0YWJhc2UuIiwgd2FybmluZyA9IEZBTFNFfQpvY2NfaWNodGh5b3NhdXJzIDwtCiAgcmVhZF9jc3YoImRhdGEvb2NjdXJyZW5jZXMvMjAyMS0wMi0xOC1pY2h0aHlvc2F1ci1vY2N1cnJlbmNlcy5jc3YiKQpvY2NfcGxvdCA8LQogIG1hcF9wbG90ICsKICAgIGdlb21fcG9pbnQoCiAgICAgIGRhdGEgPSBvY2NfaWNodGh5b3NhdXJzLAogICAgICBhZXMoCiAgICAgICAgeCA9IHBhbGVvbG5nLAogICAgICAgIHkgPSBwYWxlb2xhdAogICAgICApCiAgICApCm9jY19wbG90CmBgYAoKVGhlIG1vcmUgZWFnbGUtZXllZCBvZiB5b3UgbWF5IG5vdGljZSB0aGF0IHNvbWUgb2YgdGhlc2UgX21hcmluZV8gaWNodGh5b3NhdXIgb2NjdXJyZW5jZXMgYXJlIGxvY2F0ZWQgcmVzb2x1dGVseSBpbiB0aGUgbWlkZGxlIG9mIGxhbmRtYXNzZXMuIFRoZXJlIGFyZSBhIGZldyBwb3NzaWJsZSByZWFzb25zIGZvciB0aGF0OgoKMS4gVGhlIHBhbGFlb2dlb2dyYXBoaWNhbCByZWNvbnN0cnVjdGlvbiBpcyB3cm9uZy4KMi4gVGhlIG1vZGVybiBsb2NhdGlvbiBmb3IgdGhlIG9jY3VycmVuY2UgaXMgd3JvbmcsIHNvIHRoZSBwYWxhZW9jb29yZGluYXRlcyBhcmUgd3JvbmcuCjMuIFRoZSBhZ2Ugb2YgdGhlIG9jY3VycmVuY2UgaXMgd3JvbmcsIHNvIGl0cyByZWNvbnN0cnVjdGVkIHBvc2l0aW9uIGlzIHdyb25nLgoKVGhlc2UgdGhyZWUgYXJlIHdvcnRoIGNvbnNpZGVyaW5nIGZ1cnRoZXIuIAoKX0luY29ycmVjdCBwYWxhZW9nZW9ncmFwaGljYWwgcmVjb25zdHJ1Y3Rpb25fIGlzIHBlcmhhcHMgdGhlIG1vc3Qgb2J2aW91cyBhbnN3ZXI6IHRoZXNlIHJlY29uc3RydWN0aW9ucyBhcmUgZGlmZmljdWx0IGFuZCB0aGVyZSB3aWxsIGFsd2F5cyBiZSBzb21lIHVuY2VydGFpbnR5IHdpdGggcmVjb25zdHJ1Y3RpbmcgdGhlIGVhcnRoIDE1NSBtaWxsaW9uIHllYXJzIGFnby4gSG93ZXZlciwgdGhlIG1ldGhvZCBvZiBAQ2FvMjAxN0JiIGluY2x1ZGVkIHBhbGFlb2Vudmlyb25tZW50cyByZWNvbnN0cnVjdGVkIHVzaW5nIFBCREIgZGF0YSB0byBpbXByb3ZlIGNvbmZpZGVuY2UgaW4gdGhlIGJvcmRlcnMgb2Ygc2VhIGFuZCBsYW5kLiBUaGUgcGFsYWVvZ2VvZ3JhcGh5IHNob3VsZCBtYXRjaCB0aGUgUEJEQiB3ZWxsIHRoZW4sIHBhcnRpY3VsYXJseSBpbiB3ZWxsLXN0dWRpZWQgYW5kIHNhbXBsZWQgcGVyaW9kcyBhbmQgbG9jYXRpb25zLiBUaGVyZSBhcmUsIGhvd2V2ZXIsIHNvbWUgcGFydHMgdGhhdCBjYW5ub3QgY2VydGFpbmx5IHJlY29uc3RydWN0ZWQgYmVjYXVzZSBvZiBhIGxhY2sgb2YgZGF0YSwgcm9jayBhdmFpbGFiaWxpdHksIG9yIGFjY2Vzc2liaWxpdHkuCgpfSW5jb3JyZWN0IG1vZGVybiBsb2NhdGlvbl8gbWF5IGJlIHRoZSBlYXNpZXN0IHRvIGRpc2NvdW50IGFzIGxvY2FsaXRpZXMgdGVuZCB0byBiZSBzcGF0aWFsbHkgY29uc3RyYWluZWQgYW5kIGlkZW50aWZpYWJsZSwgZXZlbiBpbiBvbGRlciBsaXRlcmF0dXJlLiBUaGlzIGJlaW5nIHRoZSBjYXNlLCB0aGVyZSBzaG91bGQgb25seSBiZSBtaW5vciBlcnJvciBpbiB0aGUgbG9jYXRpb25zIG9mIG9jY3VycmVuY2VzICh+MTBzIGttKS4KCl9JbmNvcnJlY3QgYWdlIGFzc2lnbm1lbnRfIGlzIHByb2JhYmx5IHRoZSBtb3N0IGxpa2VseSBjYXVzZSwgb3IgcGVyaGFwcyBiZXR0ZXIgX2ltcHJlY2lzZV8gYWdlcy4gVGhlIHBhbGFlb2Nvb3JkaW5hdGVzIGNhbGN1bGF0ZWQgZm9yIGVhY2ggb2NjdXJyZW5jZSBpbiB0aGUgUEJEQiB1c2UgdGhlIG1pZHBvaW50IG9mIGl0cyB0ZW1wb3JhbCByYW5nZS4gU29tZXRpbWVzIHRoaXMgcmFuZ2UgY2FuIGJlIHF1aXRlIGxhcmdlLCBzdWNoIGFzIHRoZSBzZXZlcmFsIHNwZWNpZXMgb2YgX1VuZG9yb3NhdXJ1c18gdGhhdCBjb3ZlciB0aGUgd2hvbGUgTGF0ZSBKdXJhc3NpYyAoMTYzLjXigJMxNDUgTWEgaW4gW0dUUyAyMDIwLzAzXShodHRwczovL3N0cmF0aWdyYXBoeS5vcmcvSUNTY2hhcnQvQ2hyb25vc3RyYXRDaGFydDIwMjAtMDMucGRmKSkuIFRob3NlIHNwZWNpbWVucyBmcm9tIFBhdGFnb25pYSB0aGF0IGFyZSBwbG90dGVkIGluIHRoZSBtaWRkbGUgb2YgdGhlIGxhbmQgYXJlIGFsc28gc2ltaWxhcmx5IGltcHJlY2lzZWx5IGRhdGVkLiBUaGVpciBwYWxhZW9jb29yZGluYXRlcyBhcmUgcmVjb25zdHJ1Y3RlZCB0byB0aGUgbWlkZGxlIEtpbW1lcmlkZ2lhbiwgd2hlcmVhcyB0aGV5IGFyZSBsaWtlbHkgbGF0ZXIgKGdpdmVuIHRoZSBvY2N1cnJlbmNlIG9mIGljaHRoeW9zYXVycyBmcm9tIHNpbWlsYXIgbG9jYXRpb25zKSB3aGVuIHRoZXJlIHdhcyBhIHNlYSB0aGVyZSB0byBzd2ltIGluLgoKQXMgdGhlIGljaHRoeW9zYXVyIG9jY3VycmVuY2VzIGluY2x1ZGVkIG92ZXIgb3ZlciAyMCBteSBvZiBlYXJ0aCBoaXN0b3J5IHRoZXJlIHdpbGwgYmUgY2hhbmdlcyBpbiBwYWxhZW9nZW9ncmFwaHksIGFuZCBzb21lIHBvdGVudGlhbGx5IGltcHJlY2lzZSBkYXRpbmcuCgpUaGUgd2F5cyBhcm91bmQgdGhpcyBhcmUgdG8gaGF2ZSBtb3JlIHRpbWUgc2xpY2VzIHdpdGggdGhlIG9jY3VycmVuY2VzIHBsb3R0ZWQsIGJ1dCB0aGlzIGNvbXBsaWNhdGVzIHRoZSBjb2RlIGFuZCBhbnkgcGxvdHMsIGFuZCBzdWNoIHByZWNpc2UgcmVjb25zdHJ1Y3Rpb25zIG1heSBub3QgYmUgYXZhaWxhYmxlLiBBbHRlcm5hdGl2ZWx5LCBhaW0gdG8gdXNlIG9ubHkgdGhlIG1vc3QgcHJlY2lzZWx5IGRhdGVkIG9jY3VycmVuY2VzLCBpZiBwb3NzaWJsZSwgYnV0IHRoaXMgY2FuIHJlbW92ZSBsb3RzIG9mIHVzZWZ1bCBkYXRhLiBVbHRpbWF0ZWx5LCB5b3UgbWF5IGp1c3QgaGF2ZSB0byBhY2NlcHQgdGhhdCBzb21lIG1hcmluZSBvY2N1cnJlbmNlcyBtYXkgYXBwZWFyIG9uIGxhbmQsIG9yIHZpY2UgdmVyc2EuCgojIyBHb2luZyBmdXJ0aGVyIDI6IHNlcGFyYXRpbmcgb2NjdXJyZW5jZXMgd2l0aCBmYWNldHMgIyMKClRoZSBmaW5hbCB0aGluZyB0aGF0IHlvdSBjYW4gdHJ5IGlzIHRoZSBfZmFjZXRpbmdfIHBvd2VyIG9mIF9nZ3Bsb3QyXyB0byBzZXBhcmF0ZSB0aGUgZGlmZmVyZW50IGdyb3VwcyBvZiBvY2N1cnJlbmNlcy4gQmVsb3csIHRoaXMgY29kZSBzcGxpdHMgb3V0IHRoZSBvY2N1cnJlbmNlcyBieSB0aGVpciBhY2NlcHRlZCB0YXhvbm9taWMgcmFuay4gTW9zdCBvY2N1cnJlbmNlcyBhcmUganVzdCBub24tZGlhZ25vc3RpYyB2ZXJ0ZWJyYWwgbWF0ZXJpYWwsIHNvIGNhbid0IGJlIGFzc2lnbmVkIHRvIGEgZmFtaWx5LCBnZW51cyBvciBzcGVjaWVzLiBUaGVzZSBhcmUgbGlzdGVkIHVuZGVyICd1bnJhbmtlZF9jbGFkZScuCgpDb252ZW5pZW50bHksIGNyZWF0aW5nIGZhY2V0cyBpcyBhcyBzaW1wbGUgYXMgYWRkaW5nIGEgc2luZ2xlIGxpbmUuCgpgYGB7ciBmYWNldF9wbG90LCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gMTQsIGZpZy5jYXAgPSAiKipPY2N1cnJlbmNlcyBvZiBpY2h0aHlvc2F1cnMgaW4gdGhlIENhbGxvdmlhbuKAk1RpdGhvbmlhbiBzZXBhcmF0ZWQgYnkgaWRlbnRpZmllZCByYW5rLioqIFBhbGFlb2dlb2dyYXBoaWNhbCBtYXBzIHNob3dzIGRpc3RyaWJ1dGlvbiBvZiBsYW5kIGluIHRoZSBLaW1tZXJpZGdpYW4gKDE1NSBNYSkgcmVjb25zdHJ1Y3RlZCBieSBAQ2FvMjAxN0JiLiIsIHdhcm5pbmcgPSBGQUxTRX0Kb2NjX3Bsb3QgKwogIGZhY2V0X3dyYXAodmFycyhhY2NlcHRlZF9yYW5rKSwgbmNvbCA9IDIpCmBgYAoKIyMgRXhlcmNpc2VzIDQgIyMKCjEuIExvb2sgYXQgdGhlIFBCREIgd2ViIEFQSSAoPGh0dHBzOi8vcGFsZW9iaW9kYi5vcmcvZGF0YTEuMj4pLiBXaGF0IG90aGVyIG9wdGlvbnMgYW5kIGRhdGEgc2V0cyBjYW4geW91IGFjY2VzcyB3aXRoIHRoaXM/IENhbiB5b3UgY3JhZnQgYSBVUkwgdGhhdCB3aWxsIGRvd25sb2FkIG9jY3VycmVuY2UgZGF0YSBvZiBUaXRob25pYW4gYml2YWx2ZXMuCiAgLSBUaGlzIGRvd25sb2FkIG1heSBub3Qgd29yayBvbiBCaW5kZXIsIHNvIHlvdSBzaG91bGQgZG8gaXQgaW4gUiBvbiB5b3VyIG93biBjb21wdXRlci4KMi4gVHJ5IGFkZGluZyBjb2xvdXIgb3Igc2hhcGVzIHRvIHlvdSBvY2N1cnJlbmNlIHBvaW50cyB1c2luZyB0aGUgb3B0aW9ucyBpbiBgZ2VvbV9wb2ludGAuCjMuIERvd25sb2FkIGFuZCBwbG90IHRoZSBwYWxhZW9nZW9ncmFwaGljYWwgbWFwIGFuZCBvY2N1cnJlbmNlcyBmb3IgeW91ciBmYXZvdXJpdGUgZ3JvdXAgaW4geW91ciBmYXZvdXJpdGUgc3RhZ2UuIEFnYWluIHRoaXMgc2hvdWxkIGJlIGRvbmUgb24geW91ciBvd24gY29tcHV0ZXIuIEJld2FyZSBpdCBtYXkgdGFrZSBhIHdoaWxlIGlmIHlvdSB3YW50IHRvIHBsb3QgbWFueSBvY2N1cnJlbmNlcy4KNC4gSXQncyBhbHNvIHVzZWZ1bCB0byBzZXBhcmF0ZSBvdXQgb2NjdXJyZW5jZXMgb24gdGhlIG1hcCwgZm9yIGluc3RhbmNlLCBjYW4geW91IGNvbG91ciBvY2N1cnJlbmNlcyB3aXRoaW4gdGhlIG91dHNpZGUgdGhlIHRyb3BpY3MgZGlmZmVyZW50bHk/IHNheSByZWQgaW4gdGhlIHRyb3BpY3MgKMKxMjMuNcKwKSBhbmQgYmx1ZSBvdXRzaWRlLgogIC0gSGludDogeW91IGNhbiB1c2UgdGhlIHBhbGFlb2Nvb3JkaW5hdGVzIG9mIHRoZSBQQkRCIGRhdGEgdG8gYXNzaWduIGEgZ3JvdXAgdG8gZWFjaCBvY2N1cnJlbmNlLCB1c2UgdGhpcyB0byBjb2xvdXIgdGhlIHBvaW50cyBpbiBgZ2VvbV9wb2ludGAuCiAgLSBIaW50OiB1c2UgYHNjYWxlX2NvbG91cl9kaXNjcmV0ZWAgdG8gY29sb3VyIHRoZSBwb2ludHMuIFNlZSB0aGUgY29kZSBvZiBgcGFsYWVvZ2VvZ19tYXBfbmljZXRpZXNgIChgZnVuY3Rpb25zL3BhbGFlb2dlb2dfbWFwX25pY2V0aWVzLlJgKSBmb3IgYW4gZXhhbXBsZSBvZiBkb2luZyB0aGlzLgoKCiMgU3VnZ2VzdGVkIHNvbHV0aW9ucyB0byBleGVyY2lzZXMgIwoKTm90IGFsbCBvZiB0aGUgZXhlcmNpc2UgcXVlc3Rpb25zIGhhdmUgYSBzaW5nbGUgb2J2aW91cyBzb2x1dGlvbiwgcGFydGljdWxhcmx5IG9uZXMgd2hlcmUgeW91IHBpY2sgeW91ciBmYXZvdXJpdGUgcGVyaW9kLiBCdXQgaGVyZSBhcmUgc29tZSBjb2RlIHN1Z2dlc3Rpb25zIGZvciB5b3UgdG8gY2hlY2sgYWdhaW5zdC4KCiMjIEV4ZXJjaXNlcyAxICMjCgoxLiBGaW5kIGFuIGV4YW1wbGUgb2YgYSBwYWxhZW9nZW9ncmFwaGljYWwgbWFwIGZvciB5b3VyIGZhdm91cml0ZSB0aW1lIHBlcmlvZC4KICAtIFlvdSBjYW4gaGF2ZSBhIGxvb2sgYXQgRGVlcCBUaW1lIE1hcHMgb3IgUGFsZW9tYXAgZm9yIGVhc3kgZXhhbXBsZXMsIG9yIHlvdSBjYW4gZmluZCBvdGhlcnMgaW4gcGFwZXJzLgoyLiBIYXZlIGEgbG9vayBpbiB0aGUgZm9sZGVyIGBHcGxhdGVzU2FtcGxlRGF0YWAgdG8gc2VlIHdoYXQgb3RoZXIgcHJvamVjdHMgYXJlIGluY2x1ZGVkLiAKICAtIFRoZSBgR3BsYXRlc1NhbXBsZURhdGEvRmVhdHVyZUNvbGxlY3Rpb25zYCBkaXJlY3RvcnkgaXMgdGhlIG9uZSB0byBsb29rIGF0LiBJdCBpbmNsdWRlczoKICAgICsgT3RoZXIgcGxhdGUgcmVjb25zdHJ1Y3Rpb25zLgogICAgKyBPY2VhbiBib3VuZGFyaWVzLgogICAgKyBQbGF0ZSBmbG93IGxpbmVzLgogICAgKyBMYXJnZSBpZ25lb3VzIHByb3ZpbmNlcyDigKYgYW1vbmcgb3RoZXIgdGhpbmdzLgoKIyMgRXhlcmNpc2VzIDIgIyMKCjEuIEhhdmUgYSBsb29rIGF0IHRoZSBbR1dTIGRvY3VtZW50YXRpb25dKDxodHRwczovL2dpdGh1Yi5jb20vR1BsYXRlcy9ncGxhdGVzX3dlYl9zZXJ2aWNlX2RvYy93aWtpPikuIFdoYXQgYWRkcmVzcyB3b3VsZCB5b3UgdXNlIHRvIGdldCByZWNvbnN0cnVjdGVkIGNvYXN0bGluZXMgaW4gdGhlIGVhcmx5IENhcmJvbmlmZXJvdXMgdXNpbmcgdGhlIEBNYXR0aGV3czIwMTZHUEMgbW9kZWw/CiAgLSBgImh0dHBzOi8vZ3dzLmdwbGF0ZXMub3JnL3JlY29uc3RydWN0L2NvYXN0bGluZXMvPyZ0aW1lPTM1MCZtb2RlbD1NQVRUSEVXUzIwMTZfbWFudGxlX3JlZiJgCiAgLSBZb3UgY291bGQgYWxzbyB1c2UgYCJNQVRUSEVXUzIwMTZfcG1hZ19yZWYiYC4gVGhlc2UgYXJlIHR3byBkaWZmZXJlbnQgcmVjb25zdHJ1Y3Rpb24gbWV0aG9kcy4KMi4gT3BlbiBhbiBleGFtcGxlIG9mIHRoZSBPR1ItR01UIGRhdGEgZnJvbSB0aGUgR1dTLCBlaXRoZXIgZnJvbSB0aGUgYGRhdGEvR1dTYCBmb2xkZXIgb3IgYnkgZG93bmxvYWRpbmcgaXQuIENhbiB5b3Ugc2VlIGhvdyB0aGUgcG9seWdvbnMgYXJlIHN0b3JlZD8gSG93IGFyZSBkaWZmZXJlbnQgcG9seWdvbnMgaWRlbnRpZmllZD8KICAtIFRoZSBtZXRhZGF0YSBhdCB0aGUgdG9wIGhhcyBsaW5lcyBiZWdpbm5pbmcgd2l0aCBgIyBAYCwgaW5jbHVkaW5nIGRlc2NyaWJpbmcgaG93IHRoZSBkYXRhIGZvciBlYWNoIHBvbHlnb24gaXMgb3JnYW5pc2VkLgogIC0gRWFjaCBwb2x5Z29uIGhhcyBhIGxpbmUgb2YgbWV0YWRhdGEgZGVzY3JpYmluZyBpdCBhbmQgYEBQYCBpbmRpY2F0aW5nIGl0J3MgYSBwb2x5Z29uLgogIC0gU29tZXRpbWVzIHdpdGhvdXQgbWV0YWRhdGEgZm9yIGVhY2ggcG9seWdvbiwgdGhleSBhcmUgaW5kaWNhdGVkIHdpdGggbnVtYmVyczogYEBQMWAsIGBAUDJgIGV0Yy4KMy4gTm93IGxvb2sgYXQgdGhlIHRpZGllZCB2ZXJzaW9uIGluIFIsIGkuZS4gYGtpbW1lcmlkZ2lhbl9jb2FzdGxpbmVzYCBvciBga2ltbWVyaWRnaWFuX3BvbHlnb25zYC4gQnkgZGVmYXVsdCB0aGlzIHdpbGwgb25seSBzaG93IHRoZSBmaXJzdCAxMCByb3dzLCBidXQgeW91IGNhbiBzZWUgbW9yZSB3aXRoLCBmb3IgZXhhbXBsZSwgYGhlYWQoa2ltbWVyaWRnaWFuX3BvbHlnb25zLCBuID0gNTBMKWAuIENhbiB5b3Ugc2VlIGhvdyB0aGUgT0dSLUdNVCBkYXRhIGlzIHRyYW5zZmVycmVkIHRvIHRoaXMgdGlkeSBmb3JtYXQ/CiAgLSBUaGUgZGF0YSBpcyBzdG9yZWQgaW4gYSB0aWJibGUgd2l0aCBlYWNoIHBvaW50IGluIGEgcm93IGFuZCBjb2x1bW5zIGZvciB0aGUgbG9uZ2l0dWRlIGFuZCBsYXRpdHVkZSBvZiB0aGUgcG9pbnRzLgogIC0gVGhlIG9yZGVyIHRoYXQgdGhlIHBvaW50cyBzaG91bGQgYmUgcGxvdCBhbmQgdGhlIHBpZWNlLCBncm91cCwgYW5kIGlkIG9mIHRoZSBwb2x5Z29uIHRoYXQgdGhleSBiZWxvbmcgdG8gYXJlIGFsc28gaW5jbHVkZWQgdG8gc2VwYXJhdGUgdGhlIHNoYXBlcy4KICAtIFRoZXNlIElEIGNvbHVtbnMgYXJlIHRha2VuIGZyb20gdGhlIG1ldGFkYXRhIGZvciBlYWNoIHBvbHlnb24gZGVmaW5lIGluIHRoZSBpbnB1dCBPR1ItR01UIGZpbGUuCjQuIFBsYXkgYXJvdW5kIHdpdGggdGhlIGlucHV0cyB0byB0aGUgcGxvdCBhYm92ZS4gSG93IG1lc3N5IG9yIGNvbG91cmZ1bCBjYW4geW91IG1ha2UgdGhpcyBtYXA/IFdoYXQgb3B0aW9ucyBhcmUgdGhlcmUgaW4gYHRoZW1lX21hcCgpYD8gKEhpbnQ6IHVzZSBgP3RoZW1lX21hcGAgdG8gc2VlLikKICAtIEknbGwgbGVhdmUgdGhpcyB0byB5b3VyIGltYWdpbmF0aW9uLgoKIyMgRXhlcmNpc2VzIDMgIyMKCjEuIFRyeSBwbG90dGluZyB0aGlzIG1hcCB3aXRoIGRpZmZlcmVudCBwcm9qZWN0aW9ucyAoc2VlIGFib3ZlIGFuZCBgP21hcHByb2plY3RgKS4gRG9lcyB0aGlzIHdvcmsgd2VsbCBmb3IgYWxsIHByb2plY3Rpb25zPyBXaHk/CiAgLSBTb21lIG9mIHRoZSBwcm9qZWN0aW9ucyBtb3ZlIHRoZSBsaW5lIG9mIDDCsCBsb25naXR1ZGUgYXdheSBmcm9tIHRoZSBjZW50cmUgb3IgaGF2ZSBwb2x5Z29ucyB0aGF0IGNyb3NzIGZyb20gb25lIHNpZGUgb2YgdGhlIG1hcCB0byB0aGUgb3RoZXIuIFRoaXMgY2FuIHNvbWV0aW1lcyBsZWFkIHRvIHNoYXBlcyBiZWluZyBkcmF3biByaWdodCBhY3Jvc3MgdGhlIG1hcC4gSSdtIG5vdCBzdXJlIHdoZXRoZXIgdGhlIGJlc3Qgd2F5IHRvIGNvdW50ZXJhY3QgdGhpcyBpcyBpbiByZWRyYXdpbmcgdGhlIHBvbHlnb25zIG9yIHNvbWV0aGluZyB0byBkbyB3aXRoIHRoZSBtYXAgcHJvamVjdGlvbi4KMi4gTG9vayBhdCB0aGUgY29kZSBiZWhpbmQgYHBhbGFlb2dlb2dfbWFwX25pY2V0aWVzKClgIChpbiBgZnVuY3Rpb25zL3BhbGFlb2dlb2dfbWFwX25pY2V0aWVzLlJgKS4gTW9kaWZ5IHRoaXMgdG8gYWRkIGRpZmZlcmVudCBsaW5lcyBvZiBsYXRpdHVkZS4gQ2FuIHlvdSBhZGQgbGluZXMgb2YgbG9uZ2l0dWRlIHRvbz8gV2hhdCBhYm91dCBjaGFuZ2luZyB0aGUgY29sb3VyPwogIC0gSW4gYHBhbGFlb2dlb2dfbWFwX25pY2V0aWVzYCBJIHN0b3JlZCB0aGUgbGluZXMgb2YgbGF0aXR1ZGUgdGhhdCBJIHdhbnQgYXMgYSB0aWJibGUgdGhhdCBkZXNjcmliZSBfbGluZSBzZWdtZW50cy5fIE5ldyBsaW5lcyBvZiBsYXRpdHVkZSBjYW4gYmUgYWRkZWQgYnkgY3JlYXRpbmcgYSBuZXcgcm93IHdpdGggdGhlIHZhbHVlIF95XyBzZXQgdG8geW91ciBsYXRpdHVkZS4gVGhlIGxpbmVzIGFyZSBkZXNjcmliZWQgYXMgZ29pbmcgZnJvbSAtMTgwwrDigJMrMTgwwrAgbG9uZ2l0dWRlIGF0IGEgc3BlY2lmaWMgc2luZ2xlIGxhdGl0dWRlLgogIC0gRm9yIGxpbmVzIG9mIGxvbmdpdHVkZSwgSSdkIHN1Z2dlc3QgdXNpbmcgYSBuZXcgYGdlb21fc2VnbWVudGAgbGF5ZXIuIE1vZGlmeSB0aGUgY29kZSBzbyB0aGF0IHRoZSBfeV8gdmFsdWUgY292ZXJzIHRoZSByYW5nZSBvZiBsYXRpdHVkZXMg4oCTOTDCsOKAkys5MMKwIGF0IGEgc3BlY2lmaWMgbG9uZ2l0dWRlLiBIZXJlJ3MgYW4gZXhhbXBsZSBkcmF3aW5nIGxpbmVzIG9mIGxvbmdpdHVkZSBhdCAtNTDCsCwgMMKwIGFuZCA3MsKwOgoKYGBge3IgZXhlcmNpc2UzLjIsIGV2YWwgPSBGQUxTRX0KZ2VvbV9zZWdtZW50KAogIGRhdGEgPSB0aWJibGUoCiAgICB4ID0gYygtNTAsIDAsIDcyKSwKICAgIHkgPSByZXAoLTkwLCAzKSwKICAgIHllbmQgPSByZXAoOTAsIDMpCiAgKSwKICBhZXMoeCA9IHgsIHhlbmQgPSB4LCB5ID0geSwgeWVuZCA9IHllbmQpLAogIGNvbG91ciA9ICJncmV5NzAiLCBzaXplID0gMC4zCikKYGBgCgozLiBDYW4geW91IHRyeSBjcmVhdGluZyB5b3VyIG93biBwYWxhZW9nZW9ncmFwaGljYWwgbWFwIGZvciB5b3VyIGZhdm91cml0ZSB0aW1lPyBUaGlzIHNob3VsZCBiZSBkb25lIG9uIHlvdXIgb3duIGNvbXB1dGVyLiBGb2xsb3cgdGhlIGluc3RydWN0aW9ucyBhYm92ZSB0byBleHBvcnQgZGF0YSBmcm9tIEdQbGF0ZXMgdGhlbiBwbG90IGluIFIsIG1ha2luZyBzdXJlIHlvdSBjaGFuZ2UgYWxsIHRoZSBmaWxlIGFuZCBvYmplY3QgbmFtZXMuCiAgLSBoZXJlIHlvdSdsbCBoYXZlIHRvIG1vZGlmeSB0aGUgY29kZSBhYm92ZSBmb3IgeW91ciBvd24gR1BsYXRlcyBkYXRhLiBSZW1lbWJlciB0byBhZGQgaW4gdGhlIGljZSBjYXBzIHRvIHRoZSBsYXllciBpZiB5b3UgcGljayBhIHRpbWUgd2l0aCB0aGVzZSwgc3VjaCBhcyB0aGUgQ2FyYm9uaWZlcm91cyBvciB0aGUgTmVvZ2VuZS4KCiMjIEV4ZXJjaXNlcyA0ICMjCgoxLiBMb29rIGF0IHRoZSBQQkRCIHdlYiBBUEkgKDxodHRwczovL3BhbGVvYmlvZGIub3JnL2RhdGExLjI+KS4gV2hhdCBvdGhlciBvcHRpb25zIGFuZCBkYXRhIHNldHMgY2FuIHlvdSBhY2Nlc3Mgd2l0aCB0aGlzPyBDYW4geW91IGNyYWZ0IGEgVVJMIHRoYXQgd2lsbCBkb3dubG9hZCBvY2N1cnJlbmNlIGRhdGEgb2YgVGl0aG9uaWFuIGJpdmFsdmVzLgogIC0gImh0dHBzOi8vcGFsZW9iaW9kYi5vcmcvZGF0YTEuMi9vY2NzL2xpc3QuY3N2P2Jhc2VfbmFtZT1CaXZhbHZpYSZpbnRlcnZhbD1UaXRob25pYW4mc2hvdz1wYWxlb2xvYyIgCjIuIFRyeSBhZGRpbmcgY29sb3VyIG9yIHNoYXBlcyB0byB5b3VyIG9jY3VycmVuY2UgcG9pbnRzIHVzaW5nIHRoZSBvcHRpb25zIGluIGBnZW9tX3BvaW50YC4KICAtIFVzZSB0aGUgYGNvbG91cmAgYW5kIGBzaGFwZWAgYXJndW1lbnRzLgozLiBEb3dubG9hZCBhbmQgcGxvdCB0aGUgcGFsYWVvZ2VvZ3JhcGhpY2FsIG1hcCBhbmQgb2NjdXJyZW5jZXMgZm9yIHlvdXIgZmF2b3VyaXRlIGdyb3VwIGluIHlvdXIgZmF2b3VyaXRlIHN0YWdlLiBBZ2FpbiB0aGlzIHNob3VsZCBiZSBkb25lIG9uIHlvdXIgb3duIGNvbXB1dGVyLiBCZXdhcmUgaXQgbWF5IHRha2UgYSB3aGlsZSBpZiB5b3Ugd2FudCB0byBwbG90IG1hbnkgb2NjdXJyZW5jZXMuCiAgLSBVc2UgdGhlIGNvZGUgYWJvdmUsIGJ1dCBjaGFuZ2UgdGhlIHJlbGV2YW50IFVSTHMgYW5kIGRhdGEgZmlsZXMgdG8geW91ciBvd24gR1BsYXRlcyBhbmQgUEJEQiBkYXRhLiBEb24ndCBmb3JnZXQgdG8gYWRkIGljZSBjYXBzIGlmIHlvdSBoYXZlIHRoZW0uCiAgLSBIb3BlZnVsbHkgdGhlIFBCREIgQVBJIHdpbGwgYmUgd29ya2luZyBhcyBpdCB3YXNuJ3Qgd2hlbiBJIHdhcyB3cml0aW5nIHRoaXMhCjQuIEl0J3MgYWxzbyB1c2VmdWwgdG8gc2VwYXJhdGUgb3V0IG9jY3VycmVuY2VzIG9uIHRoZSBtYXAsIGZvciBpbnN0YW5jZSwgY2FuIHlvdSBjb2xvdXIgb2NjdXJyZW5jZXMgd2l0aGluIHRoZSBvdXRzaWRlIHRoZSB0cm9waWNzIGRpZmZlcmVudGx5PyBzYXkgcmVkIGluIHRoZSB0cm9waWNzICjCsTIzLjXCsCkgYW5kIGJsdWUgb3V0c2lkZS4KICAtIFlvdSdsbCBuZWVkIHRvIGFzc2lnbiBhIGNvbHVtbiBvZiBpZGVudGlmaWVycyBmb3IgdHJvcGljYWwgYW5kIG5vbi10cm9waWNhbCBvY2N1cnJlbmNlcy4gQmVsb3cgaXMgYW4gZXhhbXBsZSB1c2luZyB0aGUgX2RwbHlyXyBmdW5jdGlvbiBgY2FzZV93aGVuYC4KICAtIFdpdGggdGhhdCBkb25lIGl0J2xsIGZpdCByaWdodCBpbnRvIHRoZSBwbG90dGluZyBjb2RlIGZyb20gZWFybGllciwgYnV0IHlvdSB3aWxsIG5lZWQgdG8gc3BlY2lmeSB0aGUgYGNvbG91cmAgYXJndW1lbnQuIElmIHlvdSB3YW50IHRvIG1ha2UgaXQgYmx1ZSBhbmQgcmVkIHRoZW4geW91IGNhbiBhZGQgYSBzY2FsZS4KCmBgYHtyIGV4ZXJjaXNlNC40LCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNCwgZmlnLmNhcCA9ICIqKk9jY3VycmVuY2VzIG9mIGljaHRoeW9zYXVycyBpbiB0aGUgQ2FsbG92aWFu4oCTVGl0aG9uaWFuIGluZGljYXRlZCBieSBsYXRpdHVkZSByZWdpb24uKiogUGFsYWVvZ2VvZ3JhcGhpY2FsIG1hcHMgc2hvd3MgZGlzdHJpYnV0aW9uIG9mIGxhbmQgaW4gdGhlIEtpbW1lcmlkZ2lhbiAoMTU1IE1hKSByZWNvbnN0cnVjdGVkIGJ5IEBDYW8yMDE3QmIuIn0KZXhfb2NjX2ljaHRoeW9zYXVycyA8LQogIG9jY19pY2h0aHlvc2F1cnMgJT4lCiAgICBtdXRhdGUoCiAgICAgIHRyb3BpY2FsID0gY2FzZV93aGVuKAogICAgICAgIHBhbGVvbGF0IDw9IDIzLjUgJiBwYWxlb2xhdCA+PSAtMjMuNSB+ICJ0cm9waWNhbCIsCiAgICAgICAgcGFsZW9sYXQgPiAyMy41IHwgcGFsZW9sYXQgPCAtMjMuNSAgIH4gIm5vbi10cm9waWNhbCIKICAgICAgKSAlPiUKICAgICAgICBhc19mYWN0b3IoKQogICAgKQoKZXhfb2NjX3Bsb3QgPC0KICBtYXBfcGxvdCArCiAgICBnZW9tX3BvaW50KAogICAgICBkYXRhID0gZXhfb2NjX2ljaHRoeW9zYXVycywKICAgICAgYWVzKAogICAgICAgIHggPSBwYWxlb2xuZywKICAgICAgICB5ID0gcGFsZW9sYXQsCiAgICAgICAgY29sb3VyID0gdHJvcGljYWwKICAgICAgKSwKICAgICAgbmEucm0gPSBUUlVFCiAgICApICsKICAgIHNjYWxlX2Rpc2NyZXRlX21hbnVhbCgKICAgICAgdmFsdWVzID0gCiAgICAgICAgYygKICAgICAgICAgICJ0cm9waWNhbCIgPSAicmVkIiwKICAgICAgICAgICJub24tdHJvcGljYWwiID0gImJsdWUiCiAgICAgICAgKSwKICAgICAgICBhZXN0aGV0aWNzID0gYygiY29sb3VyIiksCiAgICAgICAgbmFtZSA9ICJMYXRpdHVkZSIKICAgICAgKQpleF9vY2NfcGxvdApgYGAKCgojIFJlZmVyZW5jZXMgIwoK